vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / syscalls.cpp
blob40d538780f5435a87f26ff3a689f00d086037354
1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2004-2010, Haiku Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
8 /*! Big case statement for dispatching syscalls, as well as the generic
9 syscall interface.
13 #include <syscalls.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include <TypeConstants.h>
20 #include <arch_config.h>
21 #include <arch/system_info.h>
22 #include <cpu.h>
23 #include <debug.h>
24 #include <disk_device_manager/ddm_userland_interface.h>
25 #include <elf.h>
26 #include <frame_buffer_console.h>
27 #include <fs/fd.h>
28 #include <fs/node_monitor.h>
29 #include <generic_syscall.h>
30 #include <int.h>
31 #include <kernel.h>
32 #include <kimage.h>
33 #include <ksignal.h>
34 #include <ksyscalls.h>
35 #include <ksystem_info.h>
36 #include <messaging.h>
37 #include <port.h>
38 #include <posix/realtime_sem.h>
39 #include <posix/xsi_message_queue.h>
40 #include <posix/xsi_semaphore.h>
41 #include <real_time_clock.h>
42 #include <safemode.h>
43 #include <sem.h>
44 #include <sys/resource.h>
45 #include <system_profiler.h>
46 #include <thread.h>
47 #include <tracing.h>
48 #include <user_atomic.h>
49 #include <user_mutex.h>
50 #include <usergroup.h>
51 #include <UserTimer.h>
52 #include <util/AutoLock.h>
53 #include <vfs.h>
54 #include <vm/vm.h>
55 #include <wait_for_objects.h>
57 #include "syscall_numbers.h"
60 typedef struct generic_syscall generic_syscall;
62 struct generic_syscall : DoublyLinkedListLinkImpl<generic_syscall> {
63 char subsystem[B_FILE_NAME_LENGTH];
64 syscall_hook hook;
65 uint32 version;
66 uint32 flags;
67 int32 use_count;
68 bool valid;
69 ConditionVariable unused_condition;
70 generic_syscall* previous;
73 typedef DoublyLinkedList<generic_syscall> GenericSyscallList;
76 static mutex sGenericSyscallLock = MUTEX_INITIALIZER("generic syscall");
77 static GenericSyscallList sGenericSyscalls;
80 #if SYSCALL_TRACING
81 static int dump_syscall_tracing(int argc, char** argv);
82 #endif
85 static generic_syscall*
86 find_generic_syscall(const char* subsystem)
88 ASSERT_LOCKED_MUTEX(&sGenericSyscallLock);
90 GenericSyscallList::Iterator iterator = sGenericSyscalls.GetIterator();
92 while (generic_syscall* syscall = iterator.Next()) {
93 if (!strcmp(syscall->subsystem, subsystem))
94 return syscall;
97 return NULL;
101 /*! Calls the generic syscall subsystem if any.
102 Also handles the special generic syscall function \c B_SYSCALL_INFO.
103 Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or
104 the subsystem does not support the requested function.
105 All other return codes are depending on the generic syscall implementation.
107 static inline status_t
108 _user_generic_syscall(const char* userSubsystem, uint32 function,
109 void* buffer, size_t bufferSize)
111 char subsystem[B_FILE_NAME_LENGTH];
113 if (!IS_USER_ADDRESS(userSubsystem)
114 || user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK)
115 return B_BAD_ADDRESS;
117 //dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function);
119 MutexLocker locker(sGenericSyscallLock);
121 generic_syscall* syscall = find_generic_syscall(subsystem);
122 if (syscall == NULL)
123 return B_NAME_NOT_FOUND;
125 if (function >= B_RESERVED_SYSCALL_BASE) {
126 if (function != B_SYSCALL_INFO) {
127 // this is all we know
128 return B_NAME_NOT_FOUND;
131 // special info syscall
132 if (bufferSize != sizeof(uint32))
133 return B_BAD_VALUE;
135 uint32 requestedVersion;
137 // retrieve old version
138 if (user_memcpy(&requestedVersion, buffer, sizeof(uint32)) != B_OK)
139 return B_BAD_ADDRESS;
140 if (requestedVersion != 0 && requestedVersion < syscall->version)
141 return B_BAD_TYPE;
143 // return current version
144 return user_memcpy(buffer, &syscall->version, sizeof(uint32));
147 while (syscall != NULL) {
148 generic_syscall* next;
150 if (syscall->valid) {
151 syscall->use_count++;
152 locker.Unlock();
154 status_t status
155 = syscall->hook(subsystem, function, buffer, bufferSize);
157 locker.Lock();
159 if (--syscall->use_count == 0)
160 syscall->unused_condition.NotifyAll();
162 if (status != B_BAD_HANDLER)
163 return status;
166 // the syscall may have been removed in the mean time
167 next = find_generic_syscall(subsystem);
168 if (next == syscall)
169 syscall = syscall->previous;
170 else
171 syscall = next;
174 return B_NAME_NOT_FOUND;
178 static inline int
179 _user_is_computer_on(void)
181 return 1;
185 // #pragma mark -
188 int32
189 syscall_dispatcher(uint32 callIndex, void* args, uint64* _returnValue)
191 bigtime_t startTime;
193 // dprintf("syscall_dispatcher: thread 0x%x call 0x%x, arg0 0x%x, arg1 0x%x arg2 0x%x arg3 0x%x arg4 0x%x\n",
194 // thread_get_current_thread_id(), call_num, arg0, arg1, arg2, arg3, arg4);
196 user_debug_pre_syscall(callIndex, args);
198 startTime = system_time();
200 switch (callIndex) {
201 // the cases are auto-generated
202 #include "syscall_dispatcher.h"
204 default:
205 *_returnValue = (uint64)B_BAD_VALUE;
208 user_debug_post_syscall(callIndex, args, *_returnValue, startTime);
210 // dprintf("syscall_dispatcher: done with syscall 0x%x\n", callIndex);
212 return B_HANDLED_INTERRUPT;
216 status_t
217 generic_syscall_init(void)
219 new(&sGenericSyscalls) GenericSyscallList;
221 #if SYSCALL_TRACING
222 add_debugger_command_etc("straced", &dump_syscall_tracing,
223 "Dump recorded syscall trace entries",
224 "Prints recorded trace entries. It is wrapper for the \"traced\"\n"
225 "command and supports all of its command line options (though\n"
226 "backward tracing doesn't really work). The difference is that if a\n"
227 "pre syscall trace entry is encountered, the corresponding post\n"
228 "syscall traced entry is also printed, even if it doesn't match the\n"
229 "given filter.\n", 0);
230 #endif // ENABLE_TRACING
232 return B_OK;
236 // #pragma mark - public API
239 status_t
240 register_generic_syscall(const char* subsystem, syscall_hook hook,
241 uint32 version, uint32 flags)
243 if (hook == NULL)
244 return B_BAD_VALUE;
246 MutexLocker _(sGenericSyscallLock);
248 generic_syscall* previous = find_generic_syscall(subsystem);
249 if (previous != NULL) {
250 if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0
251 || version < previous->version) {
252 return B_NAME_IN_USE;
254 if ((previous->flags & B_SYSCALL_NOT_REPLACEABLE) != 0)
255 return B_NOT_ALLOWED;
258 generic_syscall* syscall = new(std::nothrow) generic_syscall;
259 if (syscall == NULL)
260 return B_NO_MEMORY;
262 strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem));
263 syscall->hook = hook;
264 syscall->version = version;
265 syscall->flags = flags;
266 syscall->use_count = 0;
267 syscall->valid = true;
268 syscall->previous = previous;
269 syscall->unused_condition.Init(syscall, "syscall unused");
271 sGenericSyscalls.Add(syscall);
273 if (previous != NULL)
274 sGenericSyscalls.Remove(previous);
276 return B_OK;
280 status_t
281 unregister_generic_syscall(const char* subsystem, uint32 version)
283 // TODO: we should only remove the syscall with the matching version
285 while (true) {
286 MutexLocker locker(sGenericSyscallLock);
288 generic_syscall* syscall = find_generic_syscall(subsystem);
289 if (syscall == NULL)
290 return B_NAME_NOT_FOUND;
292 syscall->valid = false;
294 if (syscall->use_count != 0) {
295 // Wait until the syscall isn't in use anymore
296 ConditionVariableEntry entry;
297 syscall->unused_condition.Add(&entry);
299 locker.Unlock();
301 entry.Wait();
302 continue;
305 if (syscall->previous != NULL) {
306 // reestablish the old syscall
307 sGenericSyscalls.Add(syscall->previous);
310 sGenericSyscalls.Remove(syscall);
311 delete syscall;
313 return B_OK;
318 // #pragma mark - syscall tracing
321 #if SYSCALL_TRACING
323 namespace SyscallTracing {
326 static const char*
327 get_syscall_name(uint32 syscall)
329 if (syscall >= (uint32)kSyscallCount)
330 return "<invalid syscall number>";
332 return kExtendedSyscallInfos[syscall].name;
336 class PreSyscall : public AbstractTraceEntry {
337 public:
338 PreSyscall(uint32 syscall, const void* parameters)
340 fSyscall(syscall),
341 fParameters(NULL)
343 if (syscall < (uint32)kSyscallCount) {
344 fParameters = alloc_tracing_buffer_memcpy(parameters,
345 kSyscallInfos[syscall].parameter_size, false);
347 // copy string parameters, if any
348 if (fParameters != NULL && syscall != SYSCALL_KTRACE_OUTPUT) {
349 int32 stringIndex = 0;
350 const extended_syscall_info& syscallInfo
351 = kExtendedSyscallInfos[fSyscall];
352 for (int i = 0; i < syscallInfo.parameter_count; i++) {
353 const syscall_parameter_info& paramInfo
354 = syscallInfo.parameters[i];
355 if (paramInfo.type != B_STRING_TYPE)
356 continue;
358 const uint8* data
359 = (uint8*)fParameters + paramInfo.offset;
360 if (stringIndex < MAX_PARAM_STRINGS) {
361 fParameterStrings[stringIndex++]
362 = alloc_tracing_buffer_strcpy(
363 *(const char**)data, 64, true);
369 Initialized();
372 virtual void AddDump(TraceOutput& out)
374 out.Print("syscall pre: %s(", get_syscall_name(fSyscall));
376 if (fParameters != NULL) {
377 int32 stringIndex = 0;
378 const extended_syscall_info& syscallInfo
379 = kExtendedSyscallInfos[fSyscall];
380 for (int i = 0; i < syscallInfo.parameter_count; i++) {
381 const syscall_parameter_info& paramInfo
382 = syscallInfo.parameters[i];
383 const uint8* data = (uint8*)fParameters + paramInfo.offset;
384 uint64 value = 0;
385 bool printValue = true;
386 switch (paramInfo.type) {
387 case B_INT8_TYPE:
388 value = *(uint8*)data;
389 break;
390 case B_INT16_TYPE:
391 value = *(uint16*)data;
392 break;
393 case B_INT32_TYPE:
394 value = *(uint32*)data;
395 break;
396 case B_INT64_TYPE:
397 value = *(uint64*)data;
398 break;
399 case B_POINTER_TYPE:
400 value = (uint64)*(void**)data;
401 break;
402 case B_STRING_TYPE:
403 if (stringIndex < MAX_PARAM_STRINGS
404 && fSyscall != SYSCALL_KTRACE_OUTPUT) {
405 out.Print("%s\"%s\"",
406 (i == 0 ? "" : ", "),
407 fParameterStrings[stringIndex++]);
408 printValue = false;
409 } else
410 value = (uint64)*(void**)data;
411 break;
414 if (printValue)
415 out.Print("%s%#" B_PRIx64, (i == 0 ? "" : ", "), value);
419 out.Print(")");
422 private:
423 enum { MAX_PARAM_STRINGS = 3 };
425 uint32 fSyscall;
426 void* fParameters;
427 const char* fParameterStrings[MAX_PARAM_STRINGS];
431 class PostSyscall : public AbstractTraceEntry {
432 public:
433 PostSyscall(uint32 syscall, uint64 returnValue)
435 fSyscall(syscall),
436 fReturnValue(returnValue)
438 Initialized();
439 #if 0
440 if (syscall < (uint32)kSyscallCount
441 && returnValue != (returnValue & 0xffffffff)
442 && kExtendedSyscallInfos[syscall].return_type.size <= 4) {
443 panic("syscall return value 64 bit although it should be 32 "
444 "bit");
446 #endif
449 virtual void AddDump(TraceOutput& out)
451 out.Print("syscall post: %s() -> %#" B_PRIx64,
452 get_syscall_name(fSyscall), fReturnValue);
455 private:
456 uint32 fSyscall;
457 uint64 fReturnValue;
460 } // namespace SyscallTracing
463 extern "C" void trace_pre_syscall(uint32 syscallNumber, const void* parameters);
465 void
466 trace_pre_syscall(uint32 syscallNumber, const void* parameters)
468 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
469 if (syscallNumber != SYSCALL_KTRACE_OUTPUT)
470 #endif
472 new(std::nothrow) SyscallTracing::PreSyscall(syscallNumber, parameters);
477 extern "C" void trace_post_syscall(int syscallNumber, uint64 returnValue);
479 void
480 trace_post_syscall(int syscallNumber, uint64 returnValue)
482 #if SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT
483 if (syscallNumber != SYSCALL_KTRACE_OUTPUT)
484 #endif
486 new(std::nothrow) SyscallTracing::PostSyscall(syscallNumber,
487 returnValue);
492 using namespace SyscallTracing;
494 class SyscallWrapperTraceFilter : public WrapperTraceFilter {
495 public:
496 virtual void Init(TraceFilter* filter, int direction, bool continued)
498 fFilter = filter;
499 fHitThreadLimit = false;
500 fDirection = direction;
502 if (!continued)
503 fPendingThreadCount = 0;
506 virtual bool Filter(const TraceEntry* _entry, LazyTraceOutput& out)
508 if (fFilter == NULL)
509 return true;
511 if (fDirection < 0)
512 return fFilter->Filter(_entry, out);
514 if (const PreSyscall* entry = dynamic_cast<const PreSyscall*>(_entry)) {
515 _RemovePendingThread(entry->ThreadID());
517 bool accepted = fFilter->Filter(entry, out);
518 if (accepted)
519 _AddPendingThread(entry->ThreadID());
520 return accepted;
522 } else if (const PostSyscall* entry
523 = dynamic_cast<const PostSyscall*>(_entry)) {
524 bool wasPending = _RemovePendingThread(entry->ThreadID());
526 return wasPending || fFilter->Filter(entry, out);
528 } else if (const AbstractTraceEntry* entry
529 = dynamic_cast<const AbstractTraceEntry*>(_entry)) {
530 bool isPending = _IsPendingThread(entry->ThreadID());
532 return isPending || fFilter->Filter(entry, out);
534 } else {
535 return fFilter->Filter(_entry, out);
539 bool HitThreadLimit() const
541 return fHitThreadLimit;
544 int Direction() const
546 return fDirection;
549 private:
550 enum {
551 MAX_PENDING_THREADS = 32
554 bool _AddPendingThread(thread_id thread)
556 int32 index = _PendingThreadIndex(thread);
557 if (index >= 0)
558 return true;
560 if (fPendingThreadCount == MAX_PENDING_THREADS) {
561 fHitThreadLimit = true;
562 return false;
565 fPendingThreads[fPendingThreadCount++] = thread;
566 return true;
569 bool _RemovePendingThread(thread_id thread)
571 int32 index = _PendingThreadIndex(thread);
572 if (index < 0)
573 return false;
575 if (index + 1 < fPendingThreadCount) {
576 memmove(fPendingThreads + index, fPendingThreads + index + 1,
577 fPendingThreadCount - index - 1);
580 fPendingThreadCount--;
581 return true;
584 bool _IsPendingThread(thread_id thread)
586 return _PendingThreadIndex(thread) >= 0;
589 int32 _PendingThreadIndex(thread_id thread)
591 for (int32 i = 0; i < fPendingThreadCount; i++) {
592 if (fPendingThreads[i] == thread)
593 return i;
595 return -1;
598 TraceFilter* fFilter;
599 thread_id fPendingThreads[MAX_PENDING_THREADS];
600 int32 fPendingThreadCount;
601 int fDirection;
602 bool fHitThreadLimit;
606 static SyscallWrapperTraceFilter sFilter;
608 static int
609 dump_syscall_tracing(int argc, char** argv)
611 new(&sFilter) SyscallWrapperTraceFilter;
612 int result = dump_tracing(argc, argv, &sFilter);
614 if (sFilter.HitThreadLimit()) {
615 kprintf("Warning: The thread buffer was too small to track all "
616 "threads!\n");
617 } else if (sFilter.HitThreadLimit()) {
618 kprintf("Warning: Can't track syscalls backwards!\n");
621 return result;
625 #endif // SYSCALL_TRACING
629 * kSyscallCount and kSyscallInfos here
631 // generated by gensyscalls
632 #include "syscall_table.h"