The Pedigree Project 0.1
advisory-lock-regressions.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/processor/Processor.h"
14
15#include <fcntl.h>
16
17#include "modules/subsys/posix/FileDescriptor.h"
18#include "modules/subsys/posix/PosixSubsystem.h"
19#include "modules/subsys/posix/file-syscalls.h"
20#include "modules/system/vfs/File.h"
21#include <sys/file.h>
22
23namespace {
24constexpr size_t LockDescriptor = 96;
25constexpr size_t FcntlCommandCount = 3;
26constexpr size_t ValidFlockOperationCount = 6;
27constexpr size_t InvalidFlockOperationCount = 5;
28
29const int FcntlCommands[FcntlCommandCount] = {F_GETLK, F_SETLK, F_SETLKW};
30const int ValidFlockOperations[ValidFlockOperationCount] = {
31 LOCK_SH, LOCK_EX, LOCK_UN, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, LOCK_UN | LOCK_NB};
32const int InvalidFlockOperations[InvalidFlockOperationCount] = {
33 0, LOCK_NB, LOCK_SH | LOCK_EX, LOCK_SH | LOCK_UN, LOCK_EX | 0x1000};
34
35struct AdvisoryLockContext {
36 AdvisoryLockContext()
37 : fcntlResults{-2, -2, -2},
38 fcntlErrors{0, 0, 0},
39 badFcntlResults{-2, -2, -2},
40 badFcntlErrors{0, 0, 0},
41 validFlockResults{-2, -2, -2, -2, -2, -2},
42 validFlockErrors{0, 0, 0, 0, 0, 0},
43 invalidFlockResults{-2, -2, -2, -2, -2},
44 invalidFlockErrors{0, 0, 0, 0, 0},
45 badFlockResult(-2),
46 badFlockError(0),
47 invalidBadFlockResult(-2),
48 invalidBadFlockError(0),
49 queryUnchanged(false),
50 returned(0) {}
51
52 int fcntlResults[FcntlCommandCount];
53 int fcntlErrors[FcntlCommandCount];
54 int badFcntlResults[FcntlCommandCount];
55 int badFcntlErrors[FcntlCommandCount];
56 int validFlockResults[ValidFlockOperationCount];
57 int validFlockErrors[ValidFlockOperationCount];
58 int invalidFlockResults[InvalidFlockOperationCount];
59 int invalidFlockErrors[InvalidFlockOperationCount];
60 int badFlockResult;
61 int badFlockError;
62 int invalidBadFlockResult;
63 int invalidBadFlockError;
64 bool queryUnchanged;
65 Atomic<size_t> returned;
66};
67
68bool closeDescriptor(PosixSubsystem* subsystem, size_t fd) {
69 DescriptorLease descriptor;
70 return subsystem->acquireFileDescriptor(fd, descriptor) &&
71 subsystem->closeFileDescriptor(fd, descriptor);
72}
73
74int advisoryLockWorker(void* parameter) {
75 AdvisoryLockContext* context = reinterpret_cast<AdvisoryLockContext*>(parameter);
76 Thread* thread = Processor::information().getCurrentThread();
77
78 struct flock request = {};
79 request.l_type = F_WRLCK;
80 request.l_whence = SEEK_SET;
81 request.l_start = 7;
82 request.l_len = 13;
83 request.l_pid = 41;
84 const struct flock original = request;
85
86 for (size_t i = 0; i < FcntlCommandCount; ++i) {
87 thread->setErrno(0);
88 context->fcntlResults[i] =
89 posix_fcntl(static_cast<int>(LockDescriptor), FcntlCommands[i], &request);
90 context->fcntlErrors[i] = thread->getErrno();
91
92 thread->setErrno(0);
93 context->badFcntlResults[i] = posix_fcntl(-1, FcntlCommands[i], &request);
94 context->badFcntlErrors[i] = thread->getErrno();
95 }
96 context->queryUnchanged = request.l_type == original.l_type &&
97 request.l_whence == original.l_whence &&
98 request.l_start == original.l_start &&
99 request.l_len == original.l_len && request.l_pid == original.l_pid;
100
101 for (size_t i = 0; i < ValidFlockOperationCount; ++i) {
102 thread->setErrno(0);
103 context->validFlockResults[i] =
104 posix_flock(static_cast<int>(LockDescriptor), ValidFlockOperations[i]);
105 context->validFlockErrors[i] = thread->getErrno();
106 }
107 for (size_t i = 0; i < InvalidFlockOperationCount; ++i) {
108 thread->setErrno(0);
109 context->invalidFlockResults[i] =
110 posix_flock(static_cast<int>(LockDescriptor), InvalidFlockOperations[i]);
111 context->invalidFlockErrors[i] = thread->getErrno();
112 }
113
114 thread->setErrno(0);
115 context->badFlockResult = posix_flock(-1, LOCK_EX | LOCK_NB);
116 context->badFlockError = thread->getErrno();
117 thread->setErrno(0);
118 context->invalidBadFlockResult = posix_flock(-1, 0);
119 context->invalidBadFlockError = thread->getErrno();
120
121 context->returned += 1;
122 return 0;
123}
124
125bool advisoryLocksFailClosed(Process* kernelProcess) {
126 Process* process = new Process(kernelProcess);
127 PosixSubsystem* subsystem = new PosixSubsystem;
128 process->setSubsystem(subsystem);
129
130 File file(String("advisory-lock-probe"), 0, 0, 0, 1, nullptr, 0, nullptr);
131 subsystem->addFileDescriptor(LockDescriptor,
132 new FileDescriptor(&file, 0, LockDescriptor, 0, O_RDWR));
133
134 AdvisoryLockContext context;
135 Thread* worker = new Thread(process, advisoryLockWorker, &context, nullptr, false, true, true);
136 worker->setName("hosted advisory lock fail-closed");
137 const bool started = worker->start();
138 const bool joined = started && worker->joinForCompletion();
139 if (!started) {
140 delete worker;
141 }
142
143 bool passed = started && joined && context.returned == 1 && context.queryUnchanged;
144 for (size_t i = 0; i < FcntlCommandCount; ++i) {
145 passed = context.fcntlResults[i] == -1 && context.fcntlErrors[i] == Error::Unimplemented &&
146 context.badFcntlResults[i] == -1 &&
147 context.badFcntlErrors[i] == Error::BadFileDescriptor && passed;
148 }
149 for (size_t i = 0; i < ValidFlockOperationCount; ++i) {
150 passed = context.validFlockResults[i] == -1 &&
151 context.validFlockErrors[i] == Error::Unimplemented && passed;
152 }
153 for (size_t i = 0; i < InvalidFlockOperationCount; ++i) {
154 passed = context.invalidFlockResults[i] == -1 &&
155 context.invalidFlockErrors[i] == Error::InvalidArgument && passed;
156 }
157 passed = context.badFlockResult == -1 && context.badFlockError == Error::BadFileDescriptor &&
158 context.invalidBadFlockResult == -1 &&
159 context.invalidBadFlockError == Error::InvalidArgument && passed;
160
161 passed = closeDescriptor(subsystem, LockDescriptor) && passed;
162 delete process;
163
164 if (!passed) {
165 ERROR(
166 "HOSTED-SYSCALL-TEST: FAIL advisory-lock-fail-closed: "
167 "unsupported locks succeeded or validation returned the wrong error");
168 return false;
169 }
170
171 NOTICE("HOSTED-SYSCALL-TEST: PASS advisory-lock-fail-closed");
172 return true;
173}
174} // namespace
175
176bool runHostedAdvisoryLockRegressions(Process* process) {
177 return advisoryLocksFailClosed(process);
178}
Definition File.h:74
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
bool closeFileDescriptor(size_t fd, const DescriptorLease &descriptor)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
static ProcessorInformation & information()
void setErrno(size_t err)
Definition Thread.h:478
size_t getErrno()
Definition Thread.h:473