| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <inttypes.h> |
| #include <lib/counters.h> |
| #include <lib/crypto/global_prng.h> |
| #include <lib/syscalls/forward.h> |
| #include <lib/user_copy/user_ptr.h> |
| #include <platform.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <trace.h> |
| #include <zircon/errors.h> |
| #include <zircon/syscalls/log.h> |
| #include <zircon/syscalls/policy.h> |
| #include <zircon/types.h> |
| |
| #include <explicit-memory/bytes.h> |
| #include <fbl/alloc_checker.h> |
| #include <fbl/ref_ptr.h> |
| #include <kernel/auto_lock.h> |
| #include <kernel/thread.h> |
| #include <ktl/algorithm.h> |
| #include <ktl/atomic.h> |
| #include <object/event_dispatcher.h> |
| #include <object/event_pair_dispatcher.h> |
| #include <object/handle.h> |
| #include <object/log_dispatcher.h> |
| #include <object/process_dispatcher.h> |
| #include <object/resource.h> |
| #include <object/thread_dispatcher.h> |
| |
| #include <ktl/enforce.h> |
| |
| #define LOCAL_TRACE 0 |
| |
| KCOUNTER(syscalls_zx_ticks_get, "syscalls.zx_ticks_get") |
| KCOUNTER(syscalls_zx_ticks_get_boot, "syscalls.zx_ticks_get_boot") |
| KCOUNTER(syscalls_zx_clock_get_monotonic, "syscalls.zx_clock_get_monotonic") |
| KCOUNTER(syscalls_zx_clock_get_boot, "syscalls.zx_clock_get_boot") |
| KCOUNTER(syscalls_zx_nanosleep, "syscalls.zx_nanosleep") |
| KCOUNTER(syscalls_zx_nanosleep_zero_duration, "syscalls.zx_nanosleep_zero_duration") |
| |
| constexpr size_t kMaxCPRNGDraw = ZX_CPRNG_DRAW_MAX_LEN; |
| constexpr size_t kMaxCPRNGSeed = ZX_CPRNG_ADD_ENTROPY_MAX_LEN; |
| |
| // zx_status_t zx_nanosleep |
| zx_status_t sys_nanosleep(zx_instant_mono_t deadline) { |
| LTRACEF("nseconds %" PRIi64 "\n", deadline); |
| kcounter_add(syscalls_zx_nanosleep, 1); |
| |
| if (deadline <= 0) { |
| kcounter_add(syscalls_zx_nanosleep_zero_duration, 1); |
| return ZX_OK; |
| } |
| |
| const zx_instant_mono_t now = current_time(); |
| const auto up = ProcessDispatcher::GetCurrent(); |
| const Deadline slackDeadline(deadline, up->GetTimerSlackPolicy()); |
| |
| ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::SLEEPING); |
| |
| // This syscall is declared as "blocking", so a higher layer will automatically |
| // retry if we return ZX_ERR_INTERNAL_INTR_RETRY. |
| return Thread::Current::SleepEtc(slackDeadline, Interruptible::Yes, now); |
| } |
| |
| zx_instant_mono_t sys_clock_get_monotonic_via_kernel() { |
| kcounter_add(syscalls_zx_clock_get_monotonic, 1); |
| return current_time(); |
| } |
| |
| zx_instant_boot_t sys_clock_get_boot_via_kernel() { |
| kcounter_add(syscalls_zx_clock_get_boot, 1); |
| return current_boot_time(); |
| } |
| |
| zx_instant_mono_ticks_t sys_ticks_get_via_kernel() { |
| kcounter_add(syscalls_zx_ticks_get, 1); |
| return current_ticks(); |
| } |
| |
| zx_instant_boot_ticks_t sys_ticks_get_boot_via_kernel() { |
| kcounter_add(syscalls_zx_ticks_get_boot, 1); |
| return current_boot_ticks(); |
| } |
| |
| // zx_status_t zx_event_create |
| zx_status_t sys_event_create(uint32_t options, zx_handle_t* out) { |
| LTRACEF("options 0x%x\n", options); |
| |
| if (options != 0u) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| zx_status_t res = up->EnforceBasicPolicy(ZX_POL_NEW_EVENT); |
| if (res != ZX_OK) |
| return res; |
| |
| KernelHandle<EventDispatcher> kernel_handle; |
| zx_rights_t rights; |
| |
| zx_status_t result = EventDispatcher::Create(options, &kernel_handle, &rights); |
| if (result != ZX_OK) { |
| return result; |
| } |
| return up->MakeAndAddHandle(ktl::move(kernel_handle), rights, out); |
| } |
| |
| // zx_status_t zx_eventpair_create |
| zx_status_t sys_eventpair_create(uint32_t options, zx_handle_t* out0, zx_handle_t* out1) { |
| if (options != 0u) // No options defined/supported yet. |
| return ZX_ERR_NOT_SUPPORTED; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| zx_status_t res = up->EnforceBasicPolicy(ZX_POL_NEW_EVENTPAIR); |
| if (res != ZX_OK) |
| return res; |
| |
| KernelHandle<EventPairDispatcher> handle0, handle1; |
| zx_rights_t rights; |
| zx_status_t result = EventPairDispatcher::Create(&handle0, &handle1, &rights); |
| |
| if (result == ZX_OK) |
| result = up->MakeAndAddHandle(ktl::move(handle0), rights, out0); |
| if (result == ZX_OK) |
| result = up->MakeAndAddHandle(ktl::move(handle1), rights, out1); |
| |
| return result; |
| } |
| |
| // zx_status_t zx_debuglog_create |
| zx_status_t sys_debuglog_create(zx_handle_t rsrc, uint32_t options, zx_handle_t* out) { |
| LTRACEF("options 0x%x\n", options); |
| // To support allowing the libc dynamic linker to emit log messages even |
| // before process bootstrap is complete, we allow creating a debuglog with |
| // options == 0 (write-only) without yet having a valid `rsrc` handle. |
| // Otherwise, we should require a valid `rsrc` handle. |
| // Inversely: if a resource handle is given, or if `options` is nonzero, |
| // require that `rsrc` be a valid debuglog resource handle. |
| if (rsrc != ZX_HANDLE_INVALID) { |
| if (zx_status_t status = validate_resource_kind_base(rsrc, ZX_RSRC_KIND_SYSTEM, |
| ZX_RSRC_SYSTEM_DEBUGLOG_BASE) != ZX_OK) |
| return status; |
| } else { |
| if (options != 0) |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| // Ensure only valid options were given provided. The only valid flag is currently |
| // ZX_LOG_FLAGS_READABLE. |
| if ((options & ZX_LOG_FLAG_READABLE) != options) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // create a Log dispatcher |
| KernelHandle<LogDispatcher> handle; |
| zx_rights_t rights; |
| zx_status_t result = LogDispatcher::Create(options, &handle, &rights); |
| if (result != ZX_OK) |
| return result; |
| |
| // by default log objects are write-only |
| // as readable logs are more expensive |
| if (options & ZX_LOG_FLAG_READABLE) { |
| rights |= ZX_RIGHT_READ; |
| } |
| |
| // create a handle and attach the dispatcher to it |
| return ProcessDispatcher::GetCurrent()->MakeAndAddHandle(ktl::move(handle), rights, out); |
| } |
| |
| // zx_status_t zx_debuglog_write |
| zx_status_t sys_debuglog_write(zx_handle_t log_handle, uint32_t options, |
| user_in_ptr<const void> ptr, size_t len) { |
| LTRACEF("log handle %x, opt %x, ptr 0x%p, len %zu\n", log_handle, options, ptr.get(), len); |
| |
| len = len > DLOG_MAX_DATA ? DLOG_MAX_DATA : len; |
| |
| if (options & (~ZX_LOG_FLAGS_MASK)) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<LogDispatcher> log; |
| zx_status_t status = |
| up->handle_table().GetDispatcherWithRights(*up, log_handle, ZX_RIGHT_WRITE, &log); |
| if (status != ZX_OK) |
| return status; |
| |
| char buf[DLOG_MAX_RECORD]; |
| if (ptr.reinterpret<const char>().copy_array_from_user(buf, len) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| return log->Write(DEBUGLOG_INFO, options, {buf, len}); |
| } |
| |
| // Converts a dlog_record_t into a zx_log_record_t and copies it out to user memory. |
| // |
| // Copies at most |len| bytes to |dst|. |
| static zx::result<size_t> CopyOutLogRecord(const dlog_record_t& internal_record, |
| user_out_ptr<zx_log_record_t> dst, size_t len) { |
| zx_log_record_t external_record{}; |
| external_record.sequence = internal_record.hdr.sequence; |
| external_record.datalen = internal_record.hdr.datalen; |
| external_record.severity = internal_record.hdr.severity; |
| external_record.flags = internal_record.hdr.flags; |
| external_record.timestamp = internal_record.hdr.timestamp; |
| external_record.pid = internal_record.hdr.pid; |
| external_record.tid = internal_record.hdr.tid; |
| |
| // The user's buffer may not be large enough to hold the zx_log_record_t let |
| // alone the flexible array member that follows. |
| // |
| // |
| zx_status_t status; |
| if (len < sizeof(zx_log_record_t)) { |
| // Not enough space to copy the whole struct so we must treat it as an array |
| // of bytes instead. |
| size_t to_copy = ktl::min(len, sizeof(external_record)); |
| auto src = reinterpret_cast<const char*>(&external_record); |
| status = dst.reinterpret<char>().copy_array_to_user(src, to_copy); |
| if (status == ZX_OK) { |
| return zx::ok(to_copy); |
| } |
| return zx::error(status); |
| } |
| |
| // There's enough space for the struct so copy it as is. By not casting to an |
| // array, we benefit from user_copy's static_asserts that verify the type |
| // (zx_log_record_t) is ABI-safe. |
| status = dst.copy_to_user(external_record); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| size_t amount_copied = sizeof(external_record); |
| len -= amount_copied; |
| |
| // Copy out as much of the flexible array member as will fit. |
| size_t to_copy = ktl::min(len, static_cast<size_t>(external_record.datalen)); |
| status = dst.reinterpret<char>() |
| .byte_offset(amount_copied) |
| .copy_array_to_user(internal_record.data, to_copy); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| amount_copied += to_copy; |
| |
| return zx::ok(amount_copied); |
| } |
| |
| // zx_status_t zx_debuglog_read |
| zx_status_t sys_debuglog_read(zx_handle_t log_handle, uint32_t options, user_out_ptr<void> ptr, |
| size_t len) { |
| LTRACEF("log handle %x, opt %x, ptr 0x%p, len %zu\n", log_handle, options, ptr.get(), len); |
| |
| if (options != 0) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<LogDispatcher> log; |
| zx_status_t status = |
| up->handle_table().GetDispatcherWithRights(*up, log_handle, ZX_RIGHT_READ, &log); |
| if (status != ZX_OK) |
| return status; |
| |
| dlog_record_t record{}; |
| size_t actual; |
| if ((status = log->Read(options, &record, &actual)) < 0) { |
| return status; |
| } |
| |
| zx::result<size_t> result = CopyOutLogRecord(record, ptr.reinterpret<zx_log_record_t>(), len); |
| if (result.is_error()) { |
| return result.error_value(); |
| } |
| |
| return static_cast<zx_status_t>(result.value()); |
| } |
| |
| // zx_status_t zx_cprng_draw_once |
| zx_status_t sys_cprng_draw_once(user_out_ptr<void> buffer, size_t len) { |
| if (len > kMaxCPRNGDraw) |
| return ZX_ERR_INVALID_ARGS; |
| |
| uint8_t kernel_buf[kMaxCPRNGDraw]; |
| // Ensure we get rid of the stack copy of the random data as this function returns. |
| explicit_memory::ZeroDtor<uint8_t> zero_guard(kernel_buf, sizeof(kernel_buf)); |
| |
| auto prng = crypto::global_prng::GetInstance(); |
| ASSERT(prng->is_thread_safe()); |
| prng->Draw(kernel_buf, len); |
| |
| if (buffer.reinterpret<uint8_t>().copy_array_to_user(kernel_buf, len) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| return ZX_OK; |
| } |
| |
| // zx_status_t zx_cprng_add_entropy |
| zx_status_t sys_cprng_add_entropy(user_in_ptr<const void> buffer, size_t buffer_size) { |
| if (buffer_size > kMaxCPRNGSeed) |
| return ZX_ERR_INVALID_ARGS; |
| |
| uint8_t kernel_buf[kMaxCPRNGSeed]; |
| // Ensure we get rid of the stack copy of the entropy as this function |
| // returns. |
| explicit_memory::ZeroDtor<uint8_t> zero_guard(kernel_buf, sizeof(kernel_buf)); |
| |
| if (buffer.reinterpret<const uint8_t>().copy_array_from_user(kernel_buf, buffer_size) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto prng = crypto::global_prng::GetInstance(); |
| ASSERT(prng->is_thread_safe()); |
| prng->AddEntropy(kernel_buf, buffer_size); |
| |
| return ZX_OK; |
| } |