2020-01-18 08:38:21 +00:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2020-01-18 22:31:29 +00:00
# include <AK/Demangle.h>
2019-08-25 16:17:05 +00:00
# include <AK/FileSystemPath.h>
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
# include <AK/ScopeGuard.h>
2019-06-07 09:43:58 +00:00
# include <AK/StdLibExtras.h>
2019-02-16 23:13:47 +00:00
# include <AK/StringBuilder.h>
2019-05-18 00:00:01 +00:00
# include <AK/Time.h>
2019-06-07 09:43:58 +00:00
# include <AK/Types.h>
2020-04-09 16:17:27 +00:00
# include <Kernel/ACPI/Parser.h>
2019-06-07 18:02:01 +00:00
# include <Kernel/Arch/i386/CPU.h>
2020-02-16 00:50:16 +00:00
# include <Kernel/Devices/BlockDevice.h>
2019-11-23 07:48:07 +00:00
# include <Kernel/Devices/KeyboardDevice.h>
2019-11-28 19:59:11 +00:00
# include <Kernel/Devices/NullDevice.h>
2019-12-26 21:31:26 +00:00
# include <Kernel/Devices/PCSpeaker.h>
2019-10-13 14:41:55 +00:00
# include <Kernel/Devices/RandomDevice.h>
2019-05-30 16:58:59 +00:00
# include <Kernel/FileSystem/Custody.h>
2019-08-15 16:13:56 +00:00
# include <Kernel/FileSystem/DevPtsFS.h>
2019-08-29 18:57:02 +00:00
# include <Kernel/FileSystem/Ext2FileSystem.h>
2019-06-07 09:43:58 +00:00
# include <Kernel/FileSystem/FIFO.h>
# include <Kernel/FileSystem/FileDescription.h>
2019-07-22 18:01:11 +00:00
# include <Kernel/FileSystem/InodeWatcher.h>
2019-08-29 18:57:02 +00:00
# include <Kernel/FileSystem/ProcFS.h>
# include <Kernel/FileSystem/TmpFS.h>
2019-06-07 09:43:58 +00:00
# include <Kernel/FileSystem/VirtualFileSystem.h>
2019-10-20 14:24:42 +00:00
# include <Kernel/Heap/kmalloc.h>
2019-08-07 19:52:43 +00:00
# include <Kernel/KBufferBuilder.h>
2019-06-07 17:29:34 +00:00
# include <Kernel/KSyms.h>
2019-12-15 20:29:26 +00:00
# include <Kernel/KernelInfoPage.h>
2019-11-28 19:59:11 +00:00
# include <Kernel/Module.h>
2019-06-02 07:50:18 +00:00
# include <Kernel/Multiboot.h>
2019-06-07 09:43:58 +00:00
# include <Kernel/Net/Socket.h>
2020-02-16 01:01:42 +00:00
# include <Kernel/PerformanceEventBuffer.h>
2019-06-07 17:29:34 +00:00
# include <Kernel/Process.h>
2019-12-11 19:36:56 +00:00
# include <Kernel/Profiling.h>
2020-04-07 15:23:37 +00:00
# include <Kernel/Ptrace.h>
2019-06-07 17:29:34 +00:00
# include <Kernel/RTC.h>
2020-01-03 11:36:30 +00:00
# include <Kernel/Random.h>
2019-06-07 17:29:34 +00:00
# include <Kernel/Scheduler.h>
2019-07-16 13:03:39 +00:00
# include <Kernel/SharedBuffer.h>
2019-06-07 17:29:34 +00:00
# include <Kernel/Syscall.h>
2019-06-07 09:43:58 +00:00
# include <Kernel/TTY/MasterPTY.h>
2020-02-16 01:01:42 +00:00
# include <Kernel/TTY/TTY.h>
2019-10-20 16:11:40 +00:00
# include <Kernel/Thread.h>
2020-03-28 08:47:16 +00:00
# include <Kernel/ThreadTracer.h>
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
# include <Kernel/Time/TimeManagement.h>
2020-02-16 00:33:41 +00:00
# include <Kernel/VM/PageDirectory.h>
2020-03-01 10:14:56 +00:00
# include <Kernel/VM/PrivateInodeVMObject.h>
2020-04-04 08:26:56 +00:00
# include <Kernel/VM/ProcessPagingScope.h>
2019-12-09 18:12:38 +00:00
# include <Kernel/VM/PurgeableVMObject.h>
2020-02-28 19:29:14 +00:00
# include <Kernel/VM/SharedInodeVMObject.h>
2020-02-09 14:47:15 +00:00
# include <LibBareMetal/IO.h>
# include <LibBareMetal/Output/Console.h>
# include <LibBareMetal/StdLib.h>
2019-06-07 09:43:58 +00:00
# include <LibC/errno_numbers.h>
2020-01-02 12:01:41 +00:00
# include <LibC/limits.h>
2019-06-07 09:43:58 +00:00
# include <LibC/signal_numbers.h>
2020-04-11 18:24:07 +00:00
# include <LibELF/Loader.h>
2020-04-11 18:32:38 +00:00
# include <LibELF/Validation.h>
2018-10-16 09:01:38 +00:00
2020-02-01 09:27:25 +00:00
//#define PROCESS_DEBUG
2019-05-19 08:24:28 +00:00
//#define DEBUG_POLL_SELECT
2018-10-23 08:12:50 +00:00
//#define DEBUG_IO
2019-03-27 14:07:12 +00:00
//#define TASK_DEBUG
//#define FORK_DEBUG
2020-01-06 20:04:57 +00:00
//#define EXEC_DEBUG
2019-07-15 18:38:41 +00:00
//#define SIGNAL_DEBUG
2019-02-16 23:13:47 +00:00
//#define SHARED_BUFFER_DEBUG
2018-11-05 12:48:07 +00:00
2020-02-16 00:27:42 +00:00
namespace Kernel {
2019-07-19 15:01:16 +00:00
static void create_signal_trampolines ( ) ;
2019-12-15 20:29:26 +00:00
static void create_kernel_info_page ( ) ;
2019-07-19 15:01:16 +00:00
2020-02-17 14:04:27 +00:00
Process * Process : : current ;
2018-10-16 09:01:38 +00:00
static pid_t next_pid ;
2018-11-07 21:15:02 +00:00
InlineLinkedList < Process > * g_processes ;
2018-10-26 07:54:29 +00:00
static String * s_hostname ;
2019-02-07 09:29:26 +00:00
static Lock * s_hostname_lock ;
2019-12-15 21:21:28 +00:00
static VirtualAddress s_info_page_address_for_userspace ;
static VirtualAddress s_info_page_address_for_kernel ;
2019-07-19 15:01:16 +00:00
VirtualAddress g_return_to_ring3_from_signal_trampoline ;
2019-11-28 19:59:11 +00:00
HashMap < String , OwnPtr < Module > > * g_modules ;
2018-10-26 12:56:21 +00:00
2019-12-22 10:51:24 +00:00
pid_t Process : : allocate_pid ( )
{
InterruptDisabler disabler ;
return next_pid + + ;
}
2018-11-01 12:15:46 +00:00
void Process : : initialize ( )
2018-10-16 09:01:38 +00:00
{
2019-11-28 19:59:11 +00:00
g_modules = new HashMap < String , OwnPtr < Module > > ;
2018-10-16 09:01:38 +00:00
next_pid = 0 ;
2018-11-07 21:15:02 +00:00
g_processes = new InlineLinkedList < Process > ;
2018-11-16 23:23:39 +00:00
s_hostname = new String ( " courage " ) ;
2019-02-07 09:29:26 +00:00
s_hostname_lock = new Lock ;
2019-07-19 15:01:16 +00:00
create_signal_trampolines ( ) ;
2019-12-15 20:29:26 +00:00
create_kernel_info_page ( ) ;
}
void Process : : update_info_page_timestamp ( const timeval & tv )
{
2019-12-15 21:21:28 +00:00
auto * info_page = ( KernelInfoPage * ) s_info_page_address_for_kernel . as_ptr ( ) ;
2019-12-15 20:29:26 +00:00
info_page - > serial + + ;
const_cast < timeval & > ( info_page - > now ) = tv ;
2018-11-02 13:06:48 +00:00
}
2019-02-03 11:33:11 +00:00
Vector < pid_t > Process : : all_pids ( )
{
Vector < pid_t > pids ;
2019-02-06 17:45:21 +00:00
InterruptDisabler disabler ;
2019-12-09 16:48:58 +00:00
pids . ensure_capacity ( ( int ) g_processes - > size_slow ( ) ) ;
2019-08-08 12:40:13 +00:00
for ( auto & process : * g_processes )
pids . append ( process . pid ( ) ) ;
2019-02-03 11:33:11 +00:00
return pids ;
}
2019-01-31 16:31:23 +00:00
Vector < Process * > Process : : all_processes ( )
2018-10-23 10:44:46 +00:00
{
2018-11-01 12:15:46 +00:00
Vector < Process * > processes ;
2019-02-06 17:45:21 +00:00
InterruptDisabler disabler ;
2019-12-09 16:48:58 +00:00
processes . ensure_capacity ( ( int ) g_processes - > size_slow ( ) ) ;
2019-08-08 12:40:13 +00:00
for ( auto & process : * g_processes )
processes . append ( & process ) ;
2018-11-01 12:15:46 +00:00
return processes ;
2018-10-23 10:44:46 +00:00
}
2019-02-27 11:32:53 +00:00
bool Process : : in_group ( gid_t gid ) const
{
2020-01-02 22:45:52 +00:00
return m_gid = = gid | | m_extra_gids . contains ( gid ) ;
2019-02-27 11:32:53 +00:00
}
2020-02-16 11:55:56 +00:00
Range Process : : allocate_range ( VirtualAddress vaddr , size_t size , size_t alignment )
2018-10-18 11:05:00 +00:00
{
2019-06-07 10:56:50 +00:00
vaddr . mask ( PAGE_MASK ) ;
2019-01-12 23:27:25 +00:00
size = PAGE_ROUND_UP ( size ) ;
2019-06-07 10:56:50 +00:00
if ( vaddr . is_null ( ) )
2020-02-16 11:55:56 +00:00
return page_directory ( ) . range_allocator ( ) . allocate_anywhere ( size , alignment ) ;
2019-06-07 10:56:50 +00:00
return page_directory ( ) . range_allocator ( ) . allocate_specific ( vaddr , size ) ;
2019-05-17 02:39:22 +00:00
}
2019-05-17 01:40:15 +00:00
2019-05-30 14:14:37 +00:00
static unsigned prot_to_region_access_flags ( int prot )
{
unsigned access = 0 ;
if ( prot & PROT_READ )
access | = Region : : Access : : Read ;
if ( prot & PROT_WRITE )
access | = Region : : Access : : Write ;
if ( prot & PROT_EXEC )
access | = Region : : Access : : Execute ;
return access ;
}
2019-12-19 18:13:44 +00:00
Region & Process : : allocate_split_region ( const Region & source_region , const Range & range , size_t offset_in_vmobject )
2019-08-29 18:57:02 +00:00
{
2020-01-25 16:57:10 +00:00
auto & region = add_region ( Region : : create_user_accessible ( range , source_region . vmobject ( ) , offset_in_vmobject , source_region . name ( ) , source_region . access ( ) ) ) ;
2020-01-29 18:24:42 +00:00
region . set_mmap ( source_region . is_mmap ( ) ) ;
region . set_stack ( source_region . is_stack ( ) ) ;
2020-01-25 16:57:10 +00:00
size_t page_offset_in_source_region = ( offset_in_vmobject - source_region . offset_in_vmobject ( ) ) / PAGE_SIZE ;
for ( size_t i = 0 ; i < region . page_count ( ) ; + + i ) {
if ( source_region . should_cow ( page_offset_in_source_region + i ) )
region . set_should_cow ( i , true ) ;
}
return region ;
2019-08-29 18:57:02 +00:00
}
2020-02-16 11:55:56 +00:00
Region * Process : : allocate_region ( const Range & range , const String & name , int prot , bool commit )
2019-05-17 02:39:22 +00:00
{
2020-02-16 11:55:56 +00:00
ASSERT ( range . is_valid ( ) ) ;
2020-03-01 10:02:22 +00:00
auto vmobject = AnonymousVMObject : : create_with_size ( range . size ( ) ) ;
auto & region = add_region ( Region : : create_user_accessible ( range , vmobject , 0 , name , prot_to_region_access_flags ( prot ) ) ) ;
2020-01-19 15:25:38 +00:00
region . map ( page_directory ( ) ) ;
2019-01-22 04:01:00 +00:00
if ( commit )
2020-01-19 15:25:38 +00:00
region . commit ( ) ;
return & region ;
2018-10-18 11:05:00 +00:00
}
2020-02-16 11:55:56 +00:00
Region * Process : : allocate_region ( VirtualAddress vaddr , size_t size , const String & name , int prot , bool commit )
{
auto range = allocate_range ( vaddr , size ) ;
if ( ! range . is_valid ( ) )
return nullptr ;
return allocate_region ( range , name , prot , commit ) ;
}
2020-03-02 09:46:24 +00:00
Region * Process : : allocate_region_with_vmobject ( const Range & range , NonnullRefPtr < VMObject > vmobject , size_t offset_in_vmobject , const String & name , int prot )
2018-11-08 20:20:09 +00:00
{
2020-02-16 11:55:56 +00:00
ASSERT ( range . is_valid ( ) ) ;
size_t end_in_vmobject = offset_in_vmobject + range . size ( ) ;
2020-02-18 09:19:32 +00:00
if ( end_in_vmobject < = offset_in_vmobject ) {
2020-02-24 22:13:33 +00:00
dbg ( ) < < " allocate_region_with_vmobject: Overflow (offset + size) " ;
2020-01-18 13:37:22 +00:00
return nullptr ;
}
if ( offset_in_vmobject > = vmobject - > size ( ) ) {
2020-02-24 22:13:33 +00:00
dbg ( ) < < " allocate_region_with_vmobject: Attempt to allocate a region with an offset past the end of its VMObject. " ;
2020-01-18 13:37:22 +00:00
return nullptr ;
}
if ( end_in_vmobject > vmobject - > size ( ) ) {
2020-02-24 22:13:33 +00:00
dbg ( ) < < " allocate_region_with_vmobject: Attempt to allocate a region with an end past the end of its VMObject. " ;
2020-01-18 13:37:22 +00:00
return nullptr ;
}
2019-12-19 18:13:44 +00:00
offset_in_vmobject & = PAGE_MASK ;
2020-03-02 09:46:24 +00:00
auto & region = add_region ( Region : : create_user_accessible ( range , move ( vmobject ) , offset_in_vmobject , name , prot_to_region_access_flags ( prot ) ) ) ;
region . map ( page_directory ( ) ) ;
return & region ;
2018-11-08 20:20:09 +00:00
}
2020-03-02 09:46:24 +00:00
Region * Process : : allocate_region_with_vmobject ( VirtualAddress vaddr , size_t size , NonnullRefPtr < VMObject > vmobject , size_t offset_in_vmobject , const String & name , int prot )
2020-02-16 11:55:56 +00:00
{
auto range = allocate_range ( vaddr , size ) ;
if ( ! range . is_valid ( ) )
return nullptr ;
2020-03-02 09:46:24 +00:00
return allocate_region_with_vmobject ( range , move ( vmobject ) , offset_in_vmobject , name , prot ) ;
2020-02-16 11:55:56 +00:00
}
2018-11-03 10:28:23 +00:00
bool Process : : deallocate_region ( Region & region )
2018-10-24 07:48:24 +00:00
{
2018-10-28 08:36:21 +00:00
InterruptDisabler disabler ;
2020-01-19 15:44:37 +00:00
if ( m_region_lookup_cache . region = = & region )
m_region_lookup_cache . region = nullptr ;
2020-02-25 13:49:47 +00:00
for ( size_t i = 0 ; i < m_regions . size ( ) ; + + i ) {
2019-06-27 11:34:28 +00:00
if ( & m_regions [ i ] = = & region ) {
2020-02-23 17:00:30 +00:00
m_regions . unstable_remove ( i ) ;
2018-10-24 07:48:24 +00:00
return true ;
}
}
return false ;
}
2019-08-29 18:57:02 +00:00
Region * Process : : region_from_range ( const Range & range )
{
2020-01-19 15:44:37 +00:00
if ( m_region_lookup_cache . range = = range & & m_region_lookup_cache . region )
return m_region_lookup_cache . region ;
2019-08-29 18:57:02 +00:00
size_t size = PAGE_ROUND_UP ( range . size ( ) ) ;
for ( auto & region : m_regions ) {
2020-01-19 15:44:37 +00:00
if ( region . vaddr ( ) = = range . base ( ) & & region . size ( ) = = size ) {
m_region_lookup_cache . range = range ;
2020-02-24 12:24:30 +00:00
m_region_lookup_cache . region = region . make_weak_ptr ( ) ;
2019-08-29 18:57:02 +00:00
return & region ;
2020-01-19 15:44:37 +00:00
}
2019-08-29 18:57:02 +00:00
}
return nullptr ;
}
Region * Process : : region_containing ( const Range & range )
2018-10-24 07:48:24 +00:00
{
for ( auto & region : m_regions ) {
2019-08-29 18:57:02 +00:00
if ( region . contains ( range ) )
2019-06-27 11:34:28 +00:00
return & region ;
2018-10-24 07:48:24 +00:00
}
return nullptr ;
}
2020-01-06 10:56:59 +00:00
int Process : : sys $ set_mmap_name ( const Syscall : : SC_set_mmap_name_params * user_params )
2018-10-28 08:57:22 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-06 10:56:59 +00:00
Syscall : : SC_set_mmap_name_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-06 10:56:59 +00:00
2020-01-18 10:34:53 +00:00
if ( params . name . length > PATH_MAX )
return - ENAMETOOLONG ;
2020-01-11 11:47:47 +00:00
auto name = validate_and_copy_string_from_user ( params . name ) ;
if ( name . is_null ( ) )
2020-01-06 10:56:59 +00:00
return - EFAULT ;
2020-01-20 12:06:41 +00:00
auto * region = region_from_range ( { VirtualAddress ( params . addr ) , params . size } ) ;
2018-10-28 08:57:22 +00:00
if ( ! region )
return - EINVAL ;
2019-11-24 11:24:16 +00:00
if ( ! region - > is_mmap ( ) )
return - EPERM ;
2020-01-06 10:56:59 +00:00
region - > set_name ( name ) ;
2018-10-28 08:57:22 +00:00
return 0 ;
}
2019-12-25 20:50:13 +00:00
static bool validate_mmap_prot ( int prot , bool map_stack )
{
bool readable = prot & PROT_READ ;
bool writable = prot & PROT_WRITE ;
bool executable = prot & PROT_EXEC ;
if ( writable & & executable )
return false ;
if ( map_stack ) {
if ( executable )
return false ;
if ( ! readable | | ! writable )
return false ;
}
return true ;
}
2020-02-28 19:47:27 +00:00
static bool validate_inode_mmap_prot ( const Process & process , int prot , const Inode & inode , bool map_shared )
2020-01-07 18:29:18 +00:00
{
auto metadata = inode . metadata ( ) ;
if ( ( prot & PROT_READ ) & & ! metadata . may_read ( process ) )
return false ;
2020-02-28 19:47:27 +00:00
if ( map_shared ) {
if ( ( prot & PROT_WRITE ) & & ! metadata . may_write ( process ) )
2020-01-18 22:31:29 +00:00
return false ;
2020-02-28 19:47:27 +00:00
InterruptDisabler disabler ;
if ( inode . shared_vmobject ( ) ) {
if ( ( prot & PROT_EXEC ) & & inode . shared_vmobject ( ) - > writable_mappings ( ) )
return false ;
if ( ( prot & PROT_WRITE ) & & inode . shared_vmobject ( ) - > executable_mappings ( ) )
return false ;
}
2020-01-18 22:31:29 +00:00
}
2020-01-07 18:29:18 +00:00
return true ;
}
2019-12-30 20:11:25 +00:00
// Carve out a virtual address range from a region and return the two regions on either side
Vector < Region * , 2 > Process : : split_region_around_range ( const Region & source_region , const Range & desired_range )
{
Range old_region_range = source_region . range ( ) ;
auto remaining_ranges_after_unmap = old_region_range . carve ( desired_range ) ;
2020-01-30 20:46:45 +00:00
2019-12-30 20:11:25 +00:00
ASSERT ( ! remaining_ranges_after_unmap . is_empty ( ) ) ;
auto make_replacement_region = [ & ] ( const Range & new_range ) - > Region & {
2020-03-22 01:02:01 +00:00
ASSERT ( old_region_range . contains ( new_range ) ) ;
2019-12-30 20:11:25 +00:00
size_t new_range_offset_in_vmobject = source_region . offset_in_vmobject ( ) + ( new_range . base ( ) . get ( ) - old_region_range . base ( ) . get ( ) ) ;
return allocate_split_region ( source_region , new_range , new_range_offset_in_vmobject ) ;
} ;
Vector < Region * , 2 > new_regions ;
for ( auto & new_range : remaining_ranges_after_unmap ) {
new_regions . unchecked_append ( & make_replacement_region ( new_range ) ) ;
}
return new_regions ;
}
2020-01-06 11:04:55 +00:00
void * Process : : sys $ mmap ( const Syscall : : SC_mmap_params * user_params )
2018-10-24 07:48:24 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-06 11:04:55 +00:00
Syscall : : SC_mmap_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return ( void * ) - EFAULT ;
2020-01-06 11:04:55 +00:00
void * addr = ( void * ) params . addr ;
size_t size = params . size ;
2020-02-16 11:55:56 +00:00
size_t alignment = params . alignment ;
2020-01-06 11:04:55 +00:00
int prot = params . prot ;
int flags = params . flags ;
int fd = params . fd ;
int offset = params . offset ;
2019-11-10 14:32:59 +00:00
2020-02-16 11:55:56 +00:00
if ( alignment & ~ PAGE_MASK )
return ( void * ) - EINVAL ;
2020-01-30 20:46:45 +00:00
if ( ! is_user_range ( VirtualAddress ( addr ) , size ) )
return ( void * ) - EFAULT ;
2020-01-11 11:47:47 +00:00
String name ;
if ( params . name . characters ) {
2020-01-18 10:34:53 +00:00
if ( params . name . length > PATH_MAX )
return ( void * ) - ENAMETOOLONG ;
2020-01-11 11:47:47 +00:00
name = validate_and_copy_string_from_user ( params . name ) ;
if ( name . is_null ( ) )
return ( void * ) - EFAULT ;
}
2019-12-25 20:50:13 +00:00
2018-11-08 11:59:16 +00:00
if ( size = = 0 )
return ( void * ) - EINVAL ;
2020-03-08 09:36:51 +00:00
if ( ( FlatPtr ) addr & ~ PAGE_MASK )
2018-11-08 11:59:16 +00:00
return ( void * ) - EINVAL ;
2019-12-25 20:50:13 +00:00
bool map_shared = flags & MAP_SHARED ;
bool map_anonymous = flags & MAP_ANONYMOUS ;
bool map_purgeable = flags & MAP_PURGEABLE ;
bool map_private = flags & MAP_PRIVATE ;
bool map_stack = flags & MAP_STACK ;
2019-12-29 05:54:10 +00:00
bool map_fixed = flags & MAP_FIXED ;
2019-12-25 20:50:13 +00:00
if ( map_shared & & map_private )
2019-10-01 17:31:55 +00:00
return ( void * ) - EINVAL ;
2019-11-17 11:11:43 +00:00
2019-12-25 20:50:13 +00:00
if ( ! map_shared & & ! map_private )
2019-12-25 10:54:16 +00:00
return ( void * ) - EINVAL ;
2019-12-25 20:50:13 +00:00
if ( ! validate_mmap_prot ( prot , map_stack ) )
2019-11-17 11:11:43 +00:00
return ( void * ) - EINVAL ;
2019-12-25 20:50:13 +00:00
if ( map_stack & & ( ! map_private | | ! map_anonymous ) )
2019-11-17 11:11:43 +00:00
return ( void * ) - EINVAL ;
2019-12-25 20:50:13 +00:00
Region * region = nullptr ;
2020-02-16 11:55:56 +00:00
auto range = allocate_range ( VirtualAddress ( addr ) , size , alignment ) ;
if ( ! range . is_valid ( ) )
return ( void * ) - ENOMEM ;
2019-12-25 20:50:13 +00:00
if ( map_purgeable ) {
2019-12-09 18:12:38 +00:00
auto vmobject = PurgeableVMObject : : create_with_size ( size ) ;
2020-02-16 11:55:56 +00:00
region = allocate_region_with_vmobject ( range , vmobject , 0 , ! name . is_null ( ) ? name : " mmap (purgeable) " , prot ) ;
2019-12-29 05:54:10 +00:00
if ( ! region & & ( ! map_fixed & & addr ! = 0 ) )
2020-01-06 11:04:55 +00:00
region = allocate_region_with_vmobject ( { } , size , vmobject , 0 , ! name . is_null ( ) ? name : " mmap (purgeable) " , prot ) ;
2019-12-25 20:50:13 +00:00
} else if ( map_anonymous ) {
2020-02-16 11:55:56 +00:00
region = allocate_region ( range , ! name . is_null ( ) ? name : " mmap " , prot , false ) ;
2019-12-29 05:54:10 +00:00
if ( ! region & & ( ! map_fixed & & addr ! = 0 ) )
2020-02-16 11:55:56 +00:00
region = allocate_region ( allocate_range ( { } , size ) , ! name . is_null ( ) ? name : " mmap " , prot , false ) ;
2019-12-25 20:50:13 +00:00
} else {
if ( offset < 0 )
return ( void * ) - EINVAL ;
if ( static_cast < size_t > ( offset ) & ~ PAGE_MASK )
return ( void * ) - EINVAL ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-12-25 20:50:13 +00:00
if ( ! description )
return ( void * ) - EBADF ;
2020-01-08 11:33:36 +00:00
if ( description - > is_directory ( ) )
return ( void * ) - ENODEV ;
2020-01-07 18:29:18 +00:00
if ( ( prot & PROT_READ ) & & ! description - > is_readable ( ) )
return ( void * ) - EACCES ;
2020-02-28 19:47:27 +00:00
if ( map_shared ) {
if ( ( prot & PROT_WRITE ) & & ! description - > is_writable ( ) )
return ( void * ) - EACCES ;
}
2020-01-07 18:29:18 +00:00
if ( description - > inode ( ) ) {
2020-02-28 19:47:27 +00:00
if ( ! validate_inode_mmap_prot ( * this , prot , * description - > inode ( ) , map_shared ) )
2020-01-07 18:29:18 +00:00
return ( void * ) - EACCES ;
}
2020-02-28 19:47:27 +00:00
auto region_or_error = description - > mmap ( * this , VirtualAddress ( addr ) , static_cast < size_t > ( offset ) , size , prot , map_shared ) ;
2019-12-29 05:54:10 +00:00
if ( region_or_error . is_error ( ) ) {
// Fail if MAP_FIXED or address is 0, retry otherwise
if ( map_fixed | | addr = = 0 )
return ( void * ) ( int ) region_or_error . error ( ) ;
2020-02-28 19:47:27 +00:00
region_or_error = description - > mmap ( * this , { } , static_cast < size_t > ( offset ) , size , prot , map_shared ) ;
2019-12-29 05:54:10 +00:00
}
2019-12-25 20:50:13 +00:00
if ( region_or_error . is_error ( ) )
return ( void * ) ( int ) region_or_error . error ( ) ;
region = region_or_error . value ( ) ;
2019-12-09 18:12:38 +00:00
}
2019-12-25 20:50:13 +00:00
if ( ! region )
return ( void * ) - ENOMEM ;
region - > set_mmap ( true ) ;
if ( map_shared )
2019-02-16 23:13:47 +00:00
region - > set_shared ( true ) ;
2019-12-25 20:50:13 +00:00
if ( map_stack )
region - > set_stack ( true ) ;
2020-01-06 11:04:55 +00:00
if ( ! name . is_null ( ) )
2019-05-19 13:54:56 +00:00
region - > set_name ( name ) ;
2019-06-07 10:56:50 +00:00
return region - > vaddr ( ) . as_ptr ( ) ;
2018-10-24 07:48:24 +00:00
}
2018-11-01 12:15:46 +00:00
int Process : : sys $ munmap ( void * addr , size_t size )
2018-10-24 07:48:24 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-30 20:46:45 +00:00
2020-01-30 20:55:49 +00:00
if ( ! size )
return - EINVAL ;
2020-01-30 20:46:45 +00:00
if ( ! is_user_range ( VirtualAddress ( addr ) , size ) )
return - EFAULT ;
2020-01-20 12:06:41 +00:00
Range range_to_unmap { VirtualAddress ( addr ) , size } ;
2019-08-29 18:57:02 +00:00
if ( auto * whole_region = region_from_range ( range_to_unmap ) ) {
2019-11-24 11:24:16 +00:00
if ( ! whole_region - > is_mmap ( ) )
return - EPERM ;
2019-08-29 18:57:02 +00:00
bool success = deallocate_region ( * whole_region ) ;
ASSERT ( success ) ;
return 0 ;
}
if ( auto * old_region = region_containing ( range_to_unmap ) ) {
2019-11-24 11:24:16 +00:00
if ( ! old_region - > is_mmap ( ) )
return - EPERM ;
2019-12-30 20:11:25 +00:00
auto new_regions = split_region_around_range ( * old_region , range_to_unmap ) ;
2019-09-27 18:17:41 +00:00
// We manually unmap the old region here, specifying that we *don't* want the VM deallocated.
2019-11-03 19:37:03 +00:00
old_region - > unmap ( Region : : ShouldDeallocateVirtualMemoryRange : : No ) ;
2019-08-29 18:57:02 +00:00
deallocate_region ( * old_region ) ;
2019-09-27 18:17:41 +00:00
// Instead we give back the unwanted VM manually.
page_directory ( ) . range_allocator ( ) . deallocate ( range_to_unmap ) ;
2019-12-30 20:11:25 +00:00
// And finally we map the new region(s) using our page directory (they were just allocated and don't have one).
2019-08-29 18:57:02 +00:00
for ( auto * new_region : new_regions ) {
2019-11-03 19:48:35 +00:00
new_region - > map ( page_directory ( ) ) ;
2019-08-29 18:57:02 +00:00
}
return 0 ;
}
// FIXME: We should also support munmap() across multiple regions. (#175)
return - EINVAL ;
2018-10-24 07:48:24 +00:00
}
2019-08-12 17:33:24 +00:00
int Process : : sys $ mprotect ( void * addr , size_t size , int prot )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-30 20:46:45 +00:00
2020-01-30 20:55:49 +00:00
if ( ! size )
return - EINVAL ;
2020-01-30 20:46:45 +00:00
if ( ! is_user_range ( VirtualAddress ( addr ) , size ) )
return - EFAULT ;
2020-01-20 12:06:41 +00:00
Range range_to_mprotect = { VirtualAddress ( addr ) , size } ;
2019-12-30 20:11:25 +00:00
if ( auto * whole_region = region_from_range ( range_to_mprotect ) ) {
if ( ! whole_region - > is_mmap ( ) )
return - EPERM ;
if ( ! validate_mmap_prot ( prot , whole_region - > is_stack ( ) ) )
return - EINVAL ;
if ( whole_region - > access ( ) = = prot_to_region_access_flags ( prot ) )
return 0 ;
2020-01-07 18:29:18 +00:00
if ( whole_region - > vmobject ( ) . is_inode ( )
2020-03-01 11:27:03 +00:00
& & ! validate_inode_mmap_prot ( * this , prot , static_cast < const InodeVMObject & > ( whole_region - > vmobject ( ) ) . inode ( ) , whole_region - > is_shared ( ) ) ) {
2020-01-07 18:29:18 +00:00
return - EACCES ;
}
2019-12-30 20:11:25 +00:00
whole_region - > set_readable ( prot & PROT_READ ) ;
whole_region - > set_writable ( prot & PROT_WRITE ) ;
whole_region - > set_executable ( prot & PROT_EXEC ) ;
whole_region - > remap ( ) ;
return 0 ;
}
// Check if we can carve out the desired range from an existing region
if ( auto * old_region = region_containing ( range_to_mprotect ) ) {
if ( ! old_region - > is_mmap ( ) )
return - EPERM ;
if ( ! validate_mmap_prot ( prot , old_region - > is_stack ( ) ) )
return - EINVAL ;
if ( old_region - > access ( ) = = prot_to_region_access_flags ( prot ) )
return 0 ;
2020-01-07 18:29:18 +00:00
if ( old_region - > vmobject ( ) . is_inode ( )
2020-03-01 11:27:03 +00:00
& & ! validate_inode_mmap_prot ( * this , prot , static_cast < const InodeVMObject & > ( old_region - > vmobject ( ) ) . inode ( ) , old_region - > is_shared ( ) ) ) {
2020-01-07 18:29:18 +00:00
return - EACCES ;
}
2019-12-30 20:11:25 +00:00
// This vector is the region(s) adjacent to our range.
// We need to allocate a new region for the range we wanted to change permission bits on.
auto adjacent_regions = split_region_around_range ( * old_region , range_to_mprotect ) ;
size_t new_range_offset_in_vmobject = old_region - > offset_in_vmobject ( ) + ( range_to_mprotect . base ( ) . get ( ) - old_region - > range ( ) . base ( ) . get ( ) ) ;
auto & new_region = allocate_split_region ( * old_region , range_to_mprotect , new_range_offset_in_vmobject ) ;
new_region . set_readable ( prot & PROT_READ ) ;
new_region . set_writable ( prot & PROT_WRITE ) ;
new_region . set_executable ( prot & PROT_EXEC ) ;
// Unmap the old region here, specifying that we *don't* want the VM deallocated.
old_region - > unmap ( Region : : ShouldDeallocateVirtualMemoryRange : : No ) ;
deallocate_region ( * old_region ) ;
// Map the new regions using our page directory (they were just allocated and don't have one).
for ( auto * adjacent_region : adjacent_regions ) {
adjacent_region - > map ( page_directory ( ) ) ;
}
new_region . map ( page_directory ( ) ) ;
return 0 ;
}
// FIXME: We should also support mprotect() across multiple regions. (#175) (#964)
return - EINVAL ;
2019-08-12 17:33:24 +00:00
}
2019-12-09 18:12:38 +00:00
int Process : : sys $ madvise ( void * address , size_t size , int advice )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-30 20:46:45 +00:00
2020-01-30 20:55:49 +00:00
if ( ! size )
return - EINVAL ;
2020-01-30 20:46:45 +00:00
if ( ! is_user_range ( VirtualAddress ( address ) , size ) )
return - EFAULT ;
2020-01-20 12:06:41 +00:00
auto * region = region_from_range ( { VirtualAddress ( address ) , size } ) ;
2019-12-09 18:12:38 +00:00
if ( ! region )
return - EINVAL ;
if ( ! region - > is_mmap ( ) )
return - EPERM ;
if ( ( advice & MADV_SET_VOLATILE ) & & ( advice & MADV_SET_NONVOLATILE ) )
return - EINVAL ;
if ( advice & MADV_SET_VOLATILE ) {
if ( ! region - > vmobject ( ) . is_purgeable ( ) )
return - EPERM ;
auto & vmobject = static_cast < PurgeableVMObject & > ( region - > vmobject ( ) ) ;
vmobject . set_volatile ( true ) ;
return 0 ;
}
if ( advice & MADV_SET_NONVOLATILE ) {
if ( ! region - > vmobject ( ) . is_purgeable ( ) )
return - EPERM ;
auto & vmobject = static_cast < PurgeableVMObject & > ( region - > vmobject ( ) ) ;
2019-12-18 19:48:58 +00:00
if ( ! vmobject . is_volatile ( ) )
return 0 ;
2019-12-09 18:12:38 +00:00
vmobject . set_volatile ( false ) ;
bool was_purged = vmobject . was_purged ( ) ;
vmobject . set_was_purged ( false ) ;
return was_purged ? 1 : 0 ;
}
2019-12-18 19:48:24 +00:00
if ( advice & MADV_GET_VOLATILE ) {
if ( ! region - > vmobject ( ) . is_purgeable ( ) )
return - EPERM ;
auto & vmobject = static_cast < PurgeableVMObject & > ( region - > vmobject ( ) ) ;
return vmobject . is_volatile ( ) ? 0 : 1 ;
}
2019-12-09 18:12:38 +00:00
return - EINVAL ;
}
2020-04-12 18:22:26 +00:00
int Process : : sys $ minherit ( void * address , size_t size , int inherit )
{
REQUIRE_PROMISE ( stdio ) ;
auto * region = region_from_range ( { VirtualAddress ( address ) , size } ) ;
if ( ! region )
return - EINVAL ;
if ( ! region - > is_mmap ( ) )
return - EINVAL ;
if ( region - > is_shared ( ) )
return - EINVAL ;
if ( ! region - > vmobject ( ) . is_anonymous ( ) )
return - EINVAL ;
switch ( inherit ) {
case MAP_INHERIT_ZERO :
region - > set_inherit_mode ( Region : : InheritMode : : ZeroedOnFork ) ;
return 0 ;
}
return - EINVAL ;
}
2019-12-29 12:16:53 +00:00
int Process : : sys $ purge ( int mode )
2019-12-09 18:12:38 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-01-02 12:37:23 +00:00
if ( ! is_superuser ( ) )
return - EPERM ;
2019-12-09 18:12:38 +00:00
int purged_page_count = 0 ;
2019-12-29 12:16:53 +00:00
if ( mode & PURGE_ALL_VOLATILE ) {
NonnullRefPtrVector < PurgeableVMObject > vmobjects ;
{
InterruptDisabler disabler ;
MM . for_each_vmobject ( [ & ] ( auto & vmobject ) {
if ( vmobject . is_purgeable ( ) )
vmobjects . append ( static_cast < PurgeableVMObject & > ( vmobject ) ) ;
return IterationDecision : : Continue ;
} ) ;
}
for ( auto & vmobject : vmobjects ) {
purged_page_count + = vmobject . purge ( ) ;
}
}
if ( mode & PURGE_ALL_CLEAN_INODE ) {
2020-02-28 19:20:35 +00:00
NonnullRefPtrVector < InodeVMObject > vmobjects ;
2019-12-29 12:16:53 +00:00
{
InterruptDisabler disabler ;
MM . for_each_vmobject ( [ & ] ( auto & vmobject ) {
if ( vmobject . is_inode ( ) )
2020-02-28 19:20:35 +00:00
vmobjects . append ( static_cast < InodeVMObject & > ( vmobject ) ) ;
2019-12-29 12:16:53 +00:00
return IterationDecision : : Continue ;
} ) ;
}
for ( auto & vmobject : vmobjects ) {
purged_page_count + = vmobject . release_all_clean_pages ( ) ;
}
2019-12-09 18:12:38 +00:00
}
return purged_page_count ;
}
2019-02-25 20:19:57 +00:00
int Process : : sys $ gethostname ( char * buffer , ssize_t size )
2018-10-26 07:54:29 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-02-25 20:19:57 +00:00
if ( size < 0 )
return - EINVAL ;
2018-11-16 15:10:59 +00:00
if ( ! validate_write ( buffer , size ) )
return - EFAULT ;
2019-02-07 09:29:26 +00:00
LOCKER ( * s_hostname_lock ) ;
2019-12-09 16:45:40 +00:00
if ( ( size_t ) size < ( s_hostname - > length ( ) + 1 ) )
2018-10-26 07:54:29 +00:00
return - ENAMETOOLONG ;
2020-01-05 17:00:15 +00:00
copy_to_user ( buffer , s_hostname - > characters ( ) , s_hostname - > length ( ) + 1 ) ;
2018-10-26 09:16:56 +00:00
return 0 ;
2018-10-26 07:54:29 +00:00
}
2020-02-15 23:15:37 +00:00
pid_t Process : : sys $ fork ( RegisterState & regs )
2018-11-02 19:41:58 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2019-12-22 10:35:02 +00:00
Thread * child_first_thread = nullptr ;
auto * child = new Process ( child_first_thread , m_name , m_uid , m_gid , m_pid , m_ring , m_cwd , m_executable , m_tty , this ) ;
2020-01-10 22:14:04 +00:00
child - > m_root_directory = m_root_directory ;
2020-01-12 18:42:01 +00:00
child - > m_root_directory_relative_to_global_root = m_root_directory_relative_to_global_root ;
2020-01-11 22:19:30 +00:00
child - > m_promises = m_promises ;
child - > m_execpromises = m_execpromises ;
2020-01-21 18:28:29 +00:00
child - > m_veil_state = m_veil_state ;
2020-01-21 08:44:32 +00:00
child - > m_unveiled_paths = m_unveiled_paths ;
2020-01-25 12:50:54 +00:00
child - > m_fds = m_fds ;
child - > m_sid = m_sid ;
child - > m_pgid = m_pgid ;
child - > m_umask = m_umask ;
2018-11-10 22:29:07 +00:00
2018-11-02 19:41:58 +00:00
# ifdef FORK_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " fork: child= " < < child ;
2018-11-02 19:41:58 +00:00
# endif
for ( auto & region : m_regions ) {
# ifdef FORK_DEBUG
2019-09-27 11:57:35 +00:00
dbg ( ) < < " fork: cloning Region{ " < < & region < < " } ' " < < region . name ( ) < < " ' @ " < < region . vaddr ( ) ;
2018-11-02 19:41:58 +00:00
# endif
2020-01-19 15:25:38 +00:00
auto & child_region = child - > add_region ( region . clone ( ) ) ;
child_region . map ( child - > page_directory ( ) ) ;
2019-09-07 13:50:44 +00:00
if ( & region = = m_master_tls_region )
2020-02-28 13:04:49 +00:00
child - > m_master_tls_region = child_region . make_weak_ptr ( ) ;
2018-11-02 19:41:58 +00:00
}
2020-01-02 22:45:52 +00:00
child - > m_extra_gids = m_extra_gids ;
2019-01-21 00:49:30 +00:00
2019-12-22 10:35:02 +00:00
auto & child_tss = child_first_thread - > m_tss ;
2019-03-23 21:03:17 +00:00
child_tss . eax = 0 ; // fork() returns 0 in the child :^)
child_tss . ebx = regs . ebx ;
child_tss . ecx = regs . ecx ;
child_tss . edx = regs . edx ;
child_tss . ebp = regs . ebp ;
2020-01-09 17:02:01 +00:00
child_tss . esp = regs . userspace_esp ;
2019-03-23 21:03:17 +00:00
child_tss . esi = regs . esi ;
child_tss . edi = regs . edi ;
child_tss . eflags = regs . eflags ;
child_tss . eip = regs . eip ;
child_tss . cs = regs . cs ;
child_tss . ds = regs . ds ;
child_tss . es = regs . es ;
child_tss . fs = regs . fs ;
child_tss . gs = regs . gs ;
2020-01-09 17:02:01 +00:00
child_tss . ss = regs . userspace_ss ;
2019-01-25 06:52:44 +00:00
2018-11-02 19:41:58 +00:00
# ifdef FORK_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " fork: child will begin executing at " < < String : : format ( " %w " , child_tss . cs ) < < " : " < < String : : format ( " %x " , child_tss . eip ) < < " with stack " < < String : : format ( " %w " , child_tss . ss ) < < " : " < < String : : format ( " %x " , child_tss . esp ) < < " , kstack " < < String : : format ( " %w " , child_tss . ss0 ) < < " : " < < String : : format ( " %x " , child_tss . esp0 ) ;
2018-11-02 19:41:58 +00:00
# endif
2018-11-09 00:25:31 +00:00
{
InterruptDisabler disabler ;
g_processes - > prepend ( child ) ;
}
2018-11-02 19:41:58 +00:00
# ifdef TASK_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Process " < < child - > pid ( ) < < " ( " < < child - > name ( ) . characters ( ) < < " ) forked from " < < m_pid < < " @ " < < String : : format ( " %p " , child_tss . eip ) ;
2018-11-02 19:41:58 +00:00
# endif
2019-03-23 21:03:17 +00:00
2019-12-22 10:35:02 +00:00
child_first_thread - > set_state ( Thread : : State : : Skip1SchedulerPass ) ;
2018-11-02 19:41:58 +00:00
return child - > pid ( ) ;
}
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
void Process : : kill_threads_except_self ( )
{
InterruptDisabler disabler ;
if ( m_thread_count < = 1 )
return ;
for_each_thread ( [ & ] ( Thread & thread ) {
if ( & thread = = Thread : : current
2020-03-01 20:16:12 +00:00
| | thread . state ( ) = = Thread : : State : : Dead
| | thread . state ( ) = = Thread : : State : : Dying )
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
return IterationDecision : : Continue ;
// At this point, we have no joiner anymore
thread . m_joiner = nullptr ;
thread . set_should_die ( ) ;
if ( thread . state ( ) ! = Thread : : State : : Dead )
thread . set_state ( Thread : : State : : Dying ) ;
return IterationDecision : : Continue ;
} ) ;
big_lock ( ) . clear_waiters ( ) ;
}
void Process : : kill_all_threads ( )
{
for_each_thread ( [ & ] ( Thread & thread ) {
thread . set_should_die ( ) ;
return IterationDecision : : Continue ;
} ) ;
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
int Process : : do_exec ( NonnullRefPtr < FileDescription > main_program_description , Vector < String > arguments , Vector < String > environment , RefPtr < FileDescription > interpreter_description )
2018-11-03 00:49:40 +00:00
{
2019-02-03 02:56:08 +00:00
ASSERT ( is_ring3 ( ) ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
auto path = main_program_description - > absolute_path ( ) ;
2020-01-21 15:14:39 +00:00
dbg ( ) < < " do_exec( " < < path < < " ) " ;
2019-04-23 20:17:01 +00:00
2019-10-20 16:11:40 +00:00
size_t total_blob_size = 0 ;
for ( auto & a : arguments )
total_blob_size + = a . length ( ) + 1 ;
for ( auto & e : environment )
total_blob_size + = e . length ( ) + 1 ;
size_t total_meta_size = sizeof ( char * ) * ( arguments . size ( ) + 1 ) + sizeof ( char * ) * ( environment . size ( ) + 1 ) ;
// FIXME: How much stack space does process startup need?
if ( ( total_blob_size + total_meta_size ) > = Thread : : default_userspace_stack_size )
return - E2BIG ;
2018-11-03 00:49:40 +00:00
auto parts = path . split ( ' / ' ) ;
2018-12-21 01:10:45 +00:00
if ( parts . is_empty ( ) )
2018-11-03 00:49:40 +00:00
return - ENOENT ;
2020-03-01 10:14:56 +00:00
auto & inode = interpreter_description ? * interpreter_description - > inode ( ) : * main_program_description - > inode ( ) ;
2020-03-01 20:09:30 +00:00
auto vmobject = SharedInodeVMObject : : create_with_inode ( inode ) ;
if ( static_cast < const SharedInodeVMObject & > ( * vmobject ) . writable_mappings ( ) ) {
dbg ( ) < < " Refusing to execute a write-mapped program " ;
return - ETXTBSY ;
}
2020-01-18 22:31:29 +00:00
2020-02-22 10:09:03 +00:00
// Disable profiling temporarily in case it's running on this process.
bool was_profiling = is_profiling ( ) ;
TemporaryChange profiling_disabler ( m_profiling , false ) ;
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
// Mark this thread as the current thread that does exec
// No other thread from this process will be scheduled to run
m_exec_tid = Thread : : current - > tid ( ) ;
2018-12-31 13:58:03 +00:00
auto old_page_directory = move ( m_page_directory ) ;
2020-01-19 15:05:42 +00:00
auto old_regions = move ( m_regions ) ;
2019-08-06 09:19:16 +00:00
m_page_directory = PageDirectory : : create_for_userspace ( * this ) ;
2018-11-09 00:25:31 +00:00
# ifdef MM_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " Process " < < pid ( ) < < " exec: PD= " < < m_page_directory . ptr ( ) < < " created " ;
2018-11-09 00:25:31 +00:00
# endif
2020-01-19 12:44:53 +00:00
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
InodeMetadata loader_metadata ;
// FIXME: Hoooo boy this is a hack if I ever saw one.
// This is the 'random' offset we're giving to our ET_DYN exectuables to start as.
// It also happens to be the static Virtual Addresss offset every static exectuable gets :)
// Without this, some assumptions by the ELF loading hooks below are severely broken.
// 0x08000000 is a verified random number chosen by random dice roll https://xkcd.com/221/
u32 totally_random_offset = interpreter_description ? 0x08000000 : 0 ;
2020-01-18 22:31:29 +00:00
// FIXME: We should be able to load both the PT_INTERP interpreter and the main program... once the RTLD is smart enough
if ( interpreter_description ) {
loader_metadata = interpreter_description - > metadata ( ) ;
// we don't need the interpreter file desciption after we've loaded (or not) it into memory
interpreter_description = nullptr ;
} else {
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
loader_metadata = main_program_description - > metadata ( ) ;
2020-01-18 22:31:29 +00:00
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
2020-03-02 09:42:53 +00:00
auto region = MM . allocate_kernel_region_with_vmobject ( * vmobject , PAGE_ROUND_UP ( loader_metadata . size ) , " ELF loading " , Region : : Access : : Read ) ;
if ( ! region )
return - ENOMEM ;
2020-03-02 05:12:27 +00:00
2019-09-27 12:19:07 +00:00
Region * master_tls_region { nullptr } ;
2019-09-07 13:50:44 +00:00
size_t master_tls_size = 0 ;
size_t master_tls_alignment = 0 ;
2020-01-19 15:05:42 +00:00
u32 entry_eip = 0 ;
2019-09-07 13:50:44 +00:00
2020-03-02 09:42:53 +00:00
MM . enter_process_paging_scope ( * this ) ;
2020-04-11 18:24:07 +00:00
OwnPtr < ELF : : Loader > loader ;
2018-11-03 00:49:40 +00:00
{
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
ArmedScopeGuard rollback_regions_guard ( [ & ] ( ) {
2020-02-17 14:04:27 +00:00
ASSERT ( Process : : current = = this ) ;
2020-01-19 12:44:53 +00:00
m_page_directory = move ( old_page_directory ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
m_regions = move ( old_regions ) ;
2020-01-19 12:44:53 +00:00
MM . enter_process_paging_scope ( * this ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
} ) ;
2020-04-11 18:24:07 +00:00
loader = make < ELF : : Loader > ( region - > vaddr ( ) . as_ptr ( ) , loader_metadata . size ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
// Load the correct executable -- either interp or main program.
// FIXME: Once we actually load both interp and main, we'll need to be more clever about this.
// In that case, both will be ET_DYN objects, so they'll both be completely relocatable.
// That means, we can put them literally anywhere in User VM space (ASLR anyone?).
// ALSO FIXME: Reminder to really really fix that 'totally random offset' business.
2019-10-20 14:24:42 +00:00
loader - > map_section_hook = [ & ] ( VirtualAddress vaddr , size_t size , size_t alignment , size_t offset_in_image , bool is_readable , bool is_writable , bool is_executable , const String & name ) - > u8 * {
2018-11-08 20:20:09 +00:00
ASSERT ( size ) ;
2018-11-09 09:03:21 +00:00
ASSERT ( alignment = = PAGE_SIZE ) ;
2019-05-30 14:14:37 +00:00
int prot = 0 ;
if ( is_readable )
prot | = PROT_READ ;
if ( is_writable )
prot | = PROT_WRITE ;
if ( is_executable )
prot | = PROT_EXEC ;
2020-03-01 20:25:23 +00:00
if ( auto * region = allocate_region_with_vmobject ( vaddr . offset ( totally_random_offset ) , size , * vmobject , offset_in_image , String ( name ) , prot ) ) {
region - > set_shared ( true ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return region - > vaddr ( ) . as_ptr ( ) ;
2020-03-01 20:25:23 +00:00
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return nullptr ;
2018-11-08 20:20:09 +00:00
} ;
2019-10-20 14:24:42 +00:00
loader - > alloc_section_hook = [ & ] ( VirtualAddress vaddr , size_t size , size_t alignment , bool is_readable , bool is_writable , const String & name ) - > u8 * {
2018-11-03 10:28:23 +00:00
ASSERT ( size ) ;
2018-11-09 09:03:21 +00:00
ASSERT ( alignment = = PAGE_SIZE ) ;
2019-05-30 14:14:37 +00:00
int prot = 0 ;
if ( is_readable )
prot | = PROT_READ ;
if ( is_writable )
prot | = PROT_WRITE ;
2020-01-15 18:25:27 +00:00
if ( auto * region = allocate_region ( vaddr . offset ( totally_random_offset ) , size , String ( name ) , prot ) )
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return region - > vaddr ( ) . as_ptr ( ) ;
return nullptr ;
2018-11-03 00:49:40 +00:00
} ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
// FIXME: Move TLS region allocation to userspace: LibC and the dynamic loader.
// LibC if we end up with a statically linked executable, and the
// dynamic loader so that it can create new TLS blocks for each shared libarary
// that gets loaded as part of DT_NEEDED processing, and via dlopen()
// If that doesn't happen quickly, at least pass the location of the TLS region
// some ELF Auxilliary Vector so the loader can use it/create new ones as necessary.
2019-09-07 13:50:44 +00:00
loader - > tls_section_hook = [ & ] ( size_t size , size_t alignment ) {
ASSERT ( size ) ;
master_tls_region = allocate_region ( { } , size , String ( ) , PROT_READ | PROT_WRITE ) ;
master_tls_size = size ;
master_tls_alignment = alignment ;
return master_tls_region - > vaddr ( ) . as_ptr ( ) ;
} ;
2019-05-16 15:18:25 +00:00
bool success = loader - > load ( ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
if ( ! success ) {
2020-03-01 19:45:39 +00:00
klog ( ) < < " do_exec: Failure loading " < < path . characters ( ) ;
2018-11-03 00:49:40 +00:00
return - ENOEXEC ;
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
// FIXME: Validate that this virtual address is within executable region,
// instead of just non-null. You could totally have a DSO with entry point of
// the beginning of the text segement.
if ( ! loader - > entry ( ) . offset ( totally_random_offset ) . get ( ) ) {
2020-03-06 14:00:44 +00:00
klog ( ) < < " do_exec: Failure loading " < < path . characters ( ) < < " , entry pointer is invalid! ( " < < loader - > entry ( ) . offset ( totally_random_offset ) < < " ) " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return - ENOEXEC ;
}
rollback_regions_guard . disarm ( ) ;
2018-11-03 00:49:40 +00:00
2019-05-31 05:02:43 +00:00
// NOTE: At this point, we've committed to the new executable.
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
entry_eip = loader - > entry ( ) . offset ( totally_random_offset ) . get ( ) ;
2020-01-06 20:04:57 +00:00
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
kill_threads_except_self ( ) ;
2020-01-06 20:04:57 +00:00
# ifdef EXEC_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Memory layout after ELF load: " ;
2020-01-06 20:04:57 +00:00
dump_regions ( ) ;
# endif
2018-11-03 00:49:40 +00:00
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
m_executable = main_program_description - > custody ( ) ;
2019-05-31 05:02:43 +00:00
2020-01-11 19:48:43 +00:00
m_promises = m_execpromises ;
2020-01-21 18:28:29 +00:00
m_veil_state = VeilState : : None ;
2020-01-21 09:46:31 +00:00
m_unveiled_paths . clear ( ) ;
2019-09-07 13:50:44 +00:00
// Copy of the master TLS region that we will clone for new threads
2020-02-28 13:04:49 +00:00
m_master_tls_region = master_tls_region - > make_weak_ptr ( ) ;
2019-09-07 13:50:44 +00:00
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
auto main_program_metadata = main_program_description - > metadata ( ) ;
if ( ! ( main_program_description - > custody ( ) - > mount_flags ( ) & MS_NOSUID ) ) {
if ( main_program_metadata . is_setuid ( ) )
m_euid = main_program_metadata . uid ;
if ( main_program_metadata . is_setgid ( ) )
m_egid = main_program_metadata . gid ;
2020-01-11 15:45:38 +00:00
}
2019-05-16 15:18:25 +00:00
2020-02-17 14:04:27 +00:00
Thread : : current - > set_default_signal_dispositions ( ) ;
Thread : : current - > m_signal_mask = 0 ;
Thread : : current - > m_pending_signals = 0 ;
2018-11-10 22:29:07 +00:00
2020-01-17 15:07:20 +00:00
m_futex_queues . clear ( ) ;
2020-02-24 11:31:14 +00:00
m_region_lookup_cache = { } ;
2020-02-22 11:27:12 +00:00
disown_all_shared_buffers ( ) ;
2020-02-25 13:49:47 +00:00
for ( size_t i = 0 ; i < m_fds . size ( ) ; + + i ) {
2018-11-13 00:36:31 +00:00
auto & daf = m_fds [ i ] ;
2019-06-13 20:03:04 +00:00
if ( daf . description & & daf . flags & FD_CLOEXEC ) {
daf . description - > close ( ) ;
2019-06-07 09:43:58 +00:00
daf = { } ;
2018-11-13 00:36:31 +00:00
}
}
2019-12-22 10:35:02 +00:00
Thread * new_main_thread = nullptr ;
2020-02-17 14:04:27 +00:00
if ( Process : : current = = this ) {
new_main_thread = Thread : : current ;
2019-12-22 10:35:02 +00:00
} else {
for_each_thread ( [ & ] ( auto & thread ) {
new_main_thread = & thread ;
return IterationDecision : : Break ;
} ) ;
}
ASSERT ( new_main_thread ) ;
2019-12-18 22:03:23 +00:00
// NOTE: We create the new stack before disabling interrupts since it will zero-fault
// and we don't want to deal with faults after this point.
2019-12-22 10:35:02 +00:00
u32 new_userspace_esp = new_main_thread - > make_userspace_stack_for_main_thread ( move ( arguments ) , move ( environment ) ) ;
2019-12-18 22:03:23 +00:00
2018-11-16 23:52:29 +00:00
// We cli() manually here because we don't want to get interrupted between do_exec() and Schedule::yield().
// The reason is that the task redirection we've set up above will be clobbered by the timer IRQ.
// If we used an InterruptDisabler that sti()'d on exit, we might timer tick'd too soon in exec().
2020-02-17 14:04:27 +00:00
if ( Process : : current = = this )
2019-02-06 16:28:14 +00:00
cli ( ) ;
2018-11-16 23:52:29 +00:00
2019-12-18 22:03:23 +00:00
// NOTE: Be careful to not trigger any page faults below!
2019-12-22 10:35:02 +00:00
Scheduler : : prepare_to_modify_tss ( * new_main_thread ) ;
2018-11-03 00:49:40 +00:00
2019-01-19 21:53:05 +00:00
m_name = parts . take_last ( ) ;
2019-12-22 10:35:02 +00:00
new_main_thread - > set_name ( m_name ) ;
auto & tss = new_main_thread - > m_tss ;
2018-11-03 00:49:40 +00:00
2019-12-22 10:35:02 +00:00
u32 old_esp0 = tss . esp0 ;
2019-03-23 21:03:17 +00:00
2019-09-07 13:50:44 +00:00
m_master_tls_size = master_tls_size ;
m_master_tls_alignment = master_tls_alignment ;
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
m_pid = new_main_thread - > tid ( ) ;
2019-12-22 10:35:02 +00:00
new_main_thread - > make_thread_specific_region ( { } ) ;
2020-02-18 12:44:27 +00:00
new_main_thread - > reset_fpu_state ( ) ;
2019-12-22 10:35:02 +00:00
memset ( & tss , 0 , sizeof ( TSS32 ) ) ;
2020-01-01 16:26:25 +00:00
tss . iomapbase = sizeof ( TSS32 ) ;
2019-12-22 10:35:02 +00:00
tss . eflags = 0x0202 ;
tss . eip = entry_eip ;
tss . cs = 0x1b ;
tss . ds = 0x23 ;
tss . es = 0x23 ;
tss . fs = 0x23 ;
tss . gs = thread_specific_selector ( ) | 3 ;
tss . ss = 0x23 ;
tss . cr3 = page_directory ( ) . cr3 ( ) ;
tss . esp = new_userspace_esp ;
tss . ss0 = 0x10 ;
tss . esp0 = old_esp0 ;
tss . ss2 = m_pid ;
2018-11-03 00:49:40 +00:00
# ifdef TASK_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Process exec'd " < < path . characters ( ) < < " @ " < < String : : format ( " %p " , tss . eip ) ;
2018-11-03 00:49:40 +00:00
# endif
2020-02-22 10:09:03 +00:00
if ( was_profiling )
2020-02-22 09:54:50 +00:00
Profiling : : did_exec ( path ) ;
2019-12-22 10:35:02 +00:00
new_main_thread - > set_state ( Thread : : State : : Skip1SchedulerPass ) ;
2020-01-12 21:53:20 +00:00
big_lock ( ) . force_unlock_if_locked ( ) ;
2018-11-09 16:59:14 +00:00
return 0 ;
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
static KResultOr < Vector < String > > find_shebang_interpreter_for_executable ( const char first_page [ ] , int nread )
2019-09-15 09:47:21 +00:00
{
2019-10-27 09:42:07 +00:00
int word_start = 2 ;
int word_length = 0 ;
2019-09-15 09:47:21 +00:00
if ( nread > 2 & & first_page [ 0 ] = = ' # ' & & first_page [ 1 ] = = ' ! ' ) {
2019-10-27 09:42:07 +00:00
Vector < String > interpreter_words ;
2019-09-15 09:47:21 +00:00
for ( int i = 2 ; i < nread ; + + i ) {
if ( first_page [ i ] = = ' \n ' ) {
break ;
}
2019-10-27 09:42:07 +00:00
if ( first_page [ i ] ! = ' ' ) {
+ + word_length ;
}
if ( first_page [ i ] = = ' ' ) {
if ( word_length > 0 ) {
interpreter_words . append ( String ( & first_page [ word_start ] , word_length ) ) ;
}
word_length = 0 ;
word_start = i + 1 ;
}
2019-09-15 09:47:21 +00:00
}
2019-10-27 09:42:07 +00:00
if ( word_length > 0 )
interpreter_words . append ( String ( & first_page [ word_start ] , word_length ) ) ;
if ( ! interpreter_words . is_empty ( ) )
return interpreter_words ;
2019-09-15 09:47:21 +00:00
}
return KResult ( - ENOEXEC ) ;
}
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
KResultOr < NonnullRefPtr < FileDescription > > Process : : find_elf_interpreter_for_executable ( const String & path , char ( & first_page ) [ PAGE_SIZE ] , int nread , size_t file_size )
{
if ( nread < ( int ) sizeof ( Elf32_Ehdr ) )
return KResult ( - ENOEXEC ) ;
auto elf_header = ( Elf32_Ehdr * ) first_page ;
2020-04-11 18:32:38 +00:00
if ( ! ELF : : validate_elf_header ( * elf_header , file_size ) ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): File has invalid ELF header " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return KResult ( - ENOEXEC ) ;
}
// Not using KResultOr here because we'll want to do the same thing in userspace in the RTLD
String interpreter_path ;
2020-04-11 18:32:38 +00:00
if ( ! ELF : : validate_program_headers ( * elf_header , file_size , ( u8 * ) first_page , nread , interpreter_path ) ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): File has invalid ELF Program headers " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return KResult ( - ENOEXEC ) ;
}
if ( ! interpreter_path . is_empty ( ) ) {
// Programs with an interpreter better be relocatable executables or we don't know what to do...
if ( elf_header - > e_type ! = ET_DYN )
return KResult ( - ENOEXEC ) ;
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): Using program interpreter " < < interpreter_path ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
auto interp_result = VFS : : the ( ) . open ( interpreter_path , O_EXEC , 0 , current_directory ( ) ) ;
if ( interp_result . is_error ( ) ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): Unable to open program interpreter " < < interpreter_path ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return interp_result . error ( ) ;
}
auto interpreter_description = interp_result . value ( ) ;
auto interp_metadata = interpreter_description - > metadata ( ) ;
ASSERT ( interpreter_description - > inode ( ) ) ;
// Validate the program interpreter as a valid elf binary.
// If your program interpreter is a #! file or something, it's time to stop playing games :)
if ( interp_metadata . size < ( int ) sizeof ( Elf32_Ehdr ) )
return KResult ( - ENOEXEC ) ;
memset ( first_page , 0 , sizeof ( first_page ) ) ;
nread = interpreter_description - > read ( ( u8 * ) & first_page , sizeof ( first_page ) ) ;
if ( nread < ( int ) sizeof ( Elf32_Ehdr ) )
return KResult ( - ENOEXEC ) ;
elf_header = ( Elf32_Ehdr * ) first_page ;
2020-04-11 18:32:38 +00:00
if ( ! ELF : : validate_elf_header ( * elf_header , interp_metadata . size ) ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): Interpreter ( " < < interpreter_description - > absolute_path ( ) < < " ) has invalid ELF header " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return KResult ( - ENOEXEC ) ;
}
// Not using KResultOr here because we'll want to do the same thing in userspace in the RTLD
String interpreter_interpreter_path ;
2020-04-11 18:32:38 +00:00
if ( ! ELF : : validate_program_headers ( * elf_header , interp_metadata . size , ( u8 * ) first_page , nread , interpreter_interpreter_path ) ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): Interpreter ( " < < interpreter_description - > absolute_path ( ) < < " ) has invalid ELF Program headers " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return KResult ( - ENOEXEC ) ;
}
if ( ! interpreter_interpreter_path . is_empty ( ) ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): Interpreter ( " < < interpreter_description - > absolute_path ( ) < < " ) has its own interpreter ( " < < interpreter_interpreter_path < < " )! No thank you! " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return KResult ( - ELOOP ) ;
}
return interpreter_description ;
}
if ( elf_header - > e_type ! = ET_EXEC ) {
// We can't exec an ET_REL, that's just an object file from the compiler
// If it's ET_DYN with no PT_INTERP, then we can't load it properly either
return KResult ( - ENOEXEC ) ;
}
// No interpreter, but, path refers to a valid elf image
return KResult ( KSuccess ) ;
}
int Process : : exec ( String path , Vector < String > arguments , Vector < String > environment , int recursion_depth )
2018-11-09 16:59:14 +00:00
{
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
if ( recursion_depth > 2 ) {
2020-02-29 11:51:44 +00:00
dbg ( ) < < " exec( " < < path < < " ): SHENANIGANS! recursed too far trying to find #! interpreter " ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return - ELOOP ;
}
// Open the file to check what kind of binary format it is
// Currently supported formats:
// - #! interpreted file
// - ELF32
// * ET_EXEC binary that just gets loaded
// * ET_DYN binary that requires a program interpreter
//
auto result = VFS : : the ( ) . open ( path , O_EXEC , 0 , current_directory ( ) ) ;
if ( result . is_error ( ) )
return result . error ( ) ;
auto description = result . value ( ) ;
auto metadata = description - > metadata ( ) ;
// Always gonna need at least 3 bytes. these are for #!X
if ( metadata . size < 3 )
return - ENOEXEC ;
ASSERT ( description - > inode ( ) ) ;
// Read the first page of the program into memory so we can validate the binfmt of it
char first_page [ PAGE_SIZE ] ;
int nread = description - > read ( ( u8 * ) & first_page , sizeof ( first_page ) ) ;
// 1) #! interpreted file
auto shebang_result = find_shebang_interpreter_for_executable ( first_page , nread ) ;
if ( ! shebang_result . is_error ( ) ) {
Vector < String > new_arguments ( shebang_result . value ( ) ) ;
2019-10-27 09:42:07 +00:00
new_arguments . append ( path ) ;
arguments . remove ( 0 ) ;
new_arguments . append ( move ( arguments ) ) ;
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
return exec ( shebang_result . value ( ) . first ( ) , move ( new_arguments ) , move ( environment ) , + + recursion_depth ) ;
2019-10-27 09:42:07 +00:00
}
2019-09-15 09:47:21 +00:00
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
// #2) ELF32 for i386
auto elf_result = find_elf_interpreter_for_executable ( path , first_page , nread , metadata . size ) ;
RefPtr < FileDescription > interpreter_description ;
// We're getting either an interpreter, an error, or KSuccess (i.e. no interpreter but file checks out)
if ( ! elf_result . is_error ( ) )
interpreter_description = elf_result . value ( ) ;
else if ( elf_result . error ( ) . is_error ( ) )
return elf_result . error ( ) ;
2018-11-09 16:59:14 +00:00
// The bulk of exec() is done by do_exec(), which ensures that all locals
// are cleaned up by the time we yield-teleport below.
Kernel: Tighten up exec/do_exec and allow for PT_INTERP iterpreters
This patch changes how exec() figures out which program image to
actually load. Previously, we opened the path to our main executable in
find_shebang_interpreter_for_executable, read the first page (or less,
if the file was smaller) and then decided whether to recurse with the
interpreter instead. We then then re-opened the main executable in
do_exec.
However, since we now want to parse the ELF header and Program Headers
of an elf image before even doing any memory region work, we can change
the way this whole process works. We open the file and read (up to) the
first page in exec() itself, then pass just the page and the amount read
to find_shebang_interpreter_for_executable. Since we now have that page
and the FileDescription for the main executable handy, we can do a few
things. First, validate the ELF header and ELF program headers for any
shenanigans. ELF32 Little Endian i386 only, please. Second, we can grab
the PT_INTERP interpreter from any ET_DYN files, and open that guy right
away if it exists. Finally, we can pass the main executable's and
optionally the PT_INTERP interpreter's file descriptions down to do_exec
and not have to feel guilty about opening the file twice.
In do_exec, we now have a choice. Are we going to load the main
executable, or the interpreter? We could load both, but it'll be way
easier for the inital pass on the RTLD if we only load the interpreter.
Then it can load the main executable itself like any old shared object,
just, the one with main in it :). Later on we can load both of them
into memory and the RTLD can relocate itself before trying to do
anything. The way it's written now the RTLD will get dibs on its
requested virtual addresses being the actual virtual addresses.
2020-01-11 01:28:02 +00:00
int rc = do_exec ( move ( description ) , move ( arguments ) , move ( environment ) , move ( interpreter_description ) ) ;
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
m_exec_tid = 0 ;
2018-11-09 16:59:14 +00:00
if ( rc < 0 )
return rc ;
2018-11-07 22:13:38 +00:00
2020-04-06 13:08:25 +00:00
if ( m_wait_for_tracer_at_next_execve ) {
ASSERT ( Thread : : current - > state ( ) = = Thread : : State : : Skip1SchedulerPass ) ;
// State::Skip1SchedulerPass is irrelevant since we block the thread
Thread : : current - > set_state ( Thread : : State : : Running ) ;
Thread : : current - > send_urgent_signal_to_self ( SIGSTOP ) ;
}
2020-02-17 14:04:27 +00:00
if ( Process : : current = = this ) {
2018-11-09 00:25:31 +00:00
Scheduler : : yield ( ) ;
2018-11-08 21:24:02 +00:00
ASSERT_NOT_REACHED ( ) ;
2018-11-07 22:13:38 +00:00
}
2018-11-03 00:49:40 +00:00
return 0 ;
}
2020-01-10 11:20:36 +00:00
int Process : : sys $ execve ( const Syscall : : SC_execve_params * user_params )
2018-11-03 09:20:23 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( exec ) ;
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
2019-02-17 09:18:25 +00:00
// NOTE: Be extremely careful with allocating any kernel memory in exec().
// On success, the kernel stack will be lost.
2020-01-10 11:20:36 +00:00
Syscall : : SC_execve_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
2020-01-10 11:20:36 +00:00
return - EFAULT ;
if ( params . arguments . length > ARG_MAX | | params . environment . length > ARG_MAX )
return - E2BIG ;
2020-03-28 08:47:16 +00:00
if ( m_wait_for_tracer_at_next_execve )
Thread : : current - > send_urgent_signal_to_self ( SIGSTOP ) ;
2020-01-18 10:43:28 +00:00
String path ;
{
auto path_arg = get_syscall_path_argument ( params . path ) ;
if ( path_arg . is_error ( ) )
return path_arg . error ( ) ;
path = path_arg . value ( ) ;
}
2020-01-10 11:20:36 +00:00
auto copy_user_strings = [ & ] ( const auto & list , auto & output ) {
if ( ! list . length )
return true ;
if ( ! validate_read_typed ( list . strings , list . length ) )
return false ;
2020-01-10 19:15:58 +00:00
Vector < Syscall : : StringArgument , 32 > strings ;
2020-01-10 11:20:36 +00:00
strings . resize ( list . length ) ;
2020-01-10 19:15:58 +00:00
copy_from_user ( strings . data ( ) , list . strings , list . length * sizeof ( Syscall : : StringArgument ) ) ;
2020-01-10 11:20:36 +00:00
for ( size_t i = 0 ; i < list . length ; + + i ) {
2020-01-25 13:14:11 +00:00
auto string = validate_and_copy_string_from_user ( strings [ i ] ) ;
if ( string . is_null ( ) )
2020-01-10 11:20:36 +00:00
return false ;
2020-01-25 13:14:11 +00:00
output . append ( move ( string ) ) ;
2018-11-03 09:20:23 +00:00
}
2020-01-10 11:20:36 +00:00
return true ;
} ;
2018-11-03 09:20:23 +00:00
Vector < String > arguments ;
2020-01-10 11:20:36 +00:00
if ( ! copy_user_strings ( params . arguments , arguments ) )
return - EFAULT ;
2019-02-17 09:18:25 +00:00
Vector < String > environment ;
2020-01-10 11:20:36 +00:00
if ( ! copy_user_strings ( params . environment , environment ) )
return - EFAULT ;
2018-11-03 09:20:23 +00:00
2019-02-17 09:18:25 +00:00
int rc = exec ( move ( path ) , move ( arguments ) , move ( environment ) ) ;
2018-11-07 22:13:38 +00:00
ASSERT ( rc < 0 ) ; // We should never continue after a successful exec!
2018-11-03 09:49:13 +00:00
return rc ;
2018-11-03 09:20:23 +00:00
}
2019-12-22 10:35:02 +00:00
Process * Process : : create_user_process ( Thread * & first_thread , const String & path , uid_t uid , gid_t gid , pid_t parent_pid , int & error , Vector < String > & & arguments , Vector < String > & & environment , TTY * tty )
2018-10-22 13:42:39 +00:00
{
auto parts = path . split ( ' / ' ) ;
2018-12-21 01:10:45 +00:00
if ( arguments . is_empty ( ) ) {
2018-11-03 09:49:13 +00:00
arguments . append ( parts . last ( ) ) ;
2018-10-25 10:06:00 +00:00
}
2019-06-21 16:37:47 +00:00
RefPtr < Custody > cwd ;
2020-01-10 22:14:04 +00:00
RefPtr < Custody > root ;
2018-10-26 12:24:11 +00:00
{
InterruptDisabler disabler ;
2020-01-10 22:14:04 +00:00
if ( auto * parent = Process : : from_pid ( parent_pid ) ) {
2019-07-11 13:38:47 +00:00
cwd = parent - > m_cwd ;
2020-01-10 22:14:04 +00:00
root = parent - > m_root_directory ;
}
2018-10-27 14:43:03 +00:00
}
2019-01-16 11:57:07 +00:00
2018-11-03 09:49:13 +00:00
if ( ! cwd )
2019-05-30 16:58:59 +00:00
cwd = VFS : : the ( ) . root_custody ( ) ;
2018-10-27 14:43:03 +00:00
2020-01-10 22:14:04 +00:00
if ( ! root )
root = VFS : : the ( ) . root_custody ( ) ;
2019-12-22 10:35:02 +00:00
auto * process = new Process ( first_thread , parts . take_last ( ) , uid , gid , parent_pid , Ring3 , move ( cwd ) , nullptr , tty ) ;
2020-01-25 12:50:54 +00:00
process - > m_fds . resize ( m_max_open_file_descriptors ) ;
auto & device_to_use_as_tty = tty ? ( CharacterDevice & ) * tty : NullDevice : : the ( ) ;
2020-01-25 16:05:02 +00:00
auto description = device_to_use_as_tty . open ( O_RDWR ) . value ( ) ;
process - > m_fds [ 0 ] . set ( * description ) ;
process - > m_fds [ 1 ] . set ( * description ) ;
process - > m_fds [ 2 ] . set ( * description ) ;
2018-10-26 09:16:56 +00:00
2018-11-03 09:49:13 +00:00
error = process - > exec ( path , move ( arguments ) , move ( environment ) ) ;
2018-12-26 20:04:27 +00:00
if ( error ! = 0 ) {
delete process ;
2018-10-22 13:42:39 +00:00
return nullptr ;
2018-12-26 20:04:27 +00:00
}
2018-11-01 10:30:48 +00:00
2018-11-09 00:25:31 +00:00
{
InterruptDisabler disabler ;
g_processes - > prepend ( process ) ;
}
2018-10-23 22:20:34 +00:00
# ifdef TASK_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Process " < < process - > pid ( ) < < " ( " < < process - > name ( ) . characters ( ) < < " ) spawned @ " < < String : : format ( " %p " , first_thread - > tss ( ) . eip ) ;
2018-10-23 22:20:34 +00:00
# endif
2018-10-25 10:06:00 +00:00
error = 0 ;
2018-11-03 09:49:13 +00:00
return process ;
2018-10-22 13:42:39 +00:00
}
2019-12-22 10:35:02 +00:00
Process * Process : : create_kernel_process ( Thread * & first_thread , String & & name , void ( * e ) ( ) )
2018-10-25 09:15:17 +00:00
{
2019-12-22 10:35:02 +00:00
auto * process = new Process ( first_thread , move ( name ) , ( uid_t ) 0 , ( gid_t ) 0 , ( pid_t ) 0 , Ring0 ) ;
2020-03-08 09:36:51 +00:00
first_thread - > tss ( ) . eip = ( FlatPtr ) e ;
2018-10-25 09:15:17 +00:00
2018-11-01 12:15:46 +00:00
if ( process - > pid ( ) ! = 0 ) {
2019-03-23 21:03:17 +00:00
InterruptDisabler disabler ;
g_processes - > prepend ( process ) ;
2018-10-25 09:15:17 +00:00
# ifdef TASK_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Kernel process " < < process - > pid ( ) < < " ( " < < process - > name ( ) . characters ( ) < < " ) spawned @ " < < String : : format ( " %p " , first_thread - > tss ( ) . eip ) ;
2018-10-25 09:15:17 +00:00
# endif
}
2019-12-22 10:35:02 +00:00
first_thread - > set_state ( Thread : : State : : Runnable ) ;
2018-11-01 12:15:46 +00:00
return process ;
2018-10-25 09:15:17 +00:00
}
2019-12-22 10:35:02 +00:00
Process : : Process ( Thread * & first_thread , const String & name , uid_t uid , gid_t gid , pid_t ppid , RingLevel ring , RefPtr < Custody > cwd , RefPtr < Custody > executable , TTY * tty , Process * fork_parent )
2018-10-22 13:42:39 +00:00
: m_name ( move ( name ) )
2019-12-22 10:51:24 +00:00
, m_pid ( allocate_pid ( ) )
2018-10-22 13:42:39 +00:00
, m_uid ( uid )
, m_gid ( gid )
2018-11-05 14:04:19 +00:00
, m_euid ( uid )
, m_egid ( gid )
2018-10-25 09:15:17 +00:00
, m_ring ( ring )
2019-05-30 18:23:50 +00:00
, m_executable ( move ( executable ) )
, m_cwd ( move ( cwd ) )
2018-10-30 12:59:29 +00:00
, m_tty ( tty )
2018-11-06 12:33:06 +00:00
, m_ppid ( ppid )
2018-10-22 13:42:39 +00:00
{
2020-02-01 09:27:25 +00:00
# ifdef PROCESS_DEBUG
2020-01-21 15:14:39 +00:00
dbg ( ) < < " Created new process " < < m_name < < " ( " < < m_pid < < " ) " ;
2020-02-01 09:27:25 +00:00
# endif
2019-03-23 21:03:17 +00:00
2019-08-06 09:19:16 +00:00
m_page_directory = PageDirectory : : create_for_userspace ( * this , fork_parent ? & fork_parent - > page_directory ( ) . range_allocator ( ) : nullptr ) ;
2019-03-23 21:03:17 +00:00
# ifdef MM_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " Process " < < pid ( ) < < " ctor: PD= " < < m_page_directory . ptr ( ) < < " created " ;
2019-03-23 21:03:17 +00:00
# endif
2019-02-04 13:06:38 +00:00
2018-11-02 19:41:58 +00:00
if ( fork_parent ) {
2020-01-25 12:50:54 +00:00
// NOTE: fork() doesn't clone all threads; the thread that called fork() becomes the only thread in the new process.
2020-02-17 14:04:27 +00:00
first_thread = Thread : : current - > clone ( * this ) ;
2018-11-02 19:41:58 +00:00
} else {
2020-01-25 12:50:54 +00:00
// NOTE: This non-forked code path is only taken when the kernel creates a process "manually" (at boot.)
first_thread = new Thread ( * this ) ;
2019-02-22 01:39:13 +00:00
}
2018-10-16 09:01:38 +00:00
}
2018-11-01 12:15:46 +00:00
Process : : ~ Process ( )
2018-10-16 09:01:38 +00:00
{
2019-12-22 10:35:02 +00:00
ASSERT ( thread_count ( ) = = 0 ) ;
2018-10-18 12:53:00 +00:00
}
2019-01-31 16:31:23 +00:00
void Process : : dump_regions ( )
2018-10-18 12:53:00 +00:00
{
2020-03-01 19:45:39 +00:00
klog ( ) < < " Process regions: " ;
klog ( ) < < " BEGIN END SIZE ACCESS NAME " ;
2018-10-18 12:53:00 +00:00
for ( auto & region : m_regions ) {
2020-03-01 19:45:39 +00:00
klog ( ) < < String : : format ( " %08x " , region . vaddr ( ) . get ( ) ) < < " -- " < < String : : format ( " %08x " , region . vaddr ( ) . offset ( region . size ( ) - 1 ) . get ( ) ) < < " " < < String : : format ( " %08x " , region . size ( ) ) < < " " < < ( region . is_readable ( ) ? ' R ' : ' ' ) < < ( region . is_writable ( ) ? ' W ' : ' ' ) < < ( region . is_executable ( ) ? ' X ' : ' ' ) < < ( region . is_shared ( ) ? ' S ' : ' ' ) < < ( region . is_stack ( ) ? ' T ' : ' ' ) < < ( region . vmobject ( ) . is_purgeable ( ) ? ' P ' : ' ' ) < < " " < < region . name ( ) . characters ( ) ;
2018-10-18 12:53:00 +00:00
}
2020-01-18 07:34:28 +00:00
MM . dump_kernel_regions ( ) ;
2018-10-16 09:01:38 +00:00
}
2018-11-01 12:15:46 +00:00
void Process : : sys $ exit ( int status )
2018-10-22 09:43:55 +00:00
{
cli ( ) ;
2018-10-23 22:20:34 +00:00
# ifdef TASK_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " sys$exit: exit with status " < < status ;
2018-10-23 22:20:34 +00:00
# endif
2018-10-22 09:43:55 +00:00
2020-01-12 15:27:24 +00:00
if ( status ! = 0 )
dump_backtrace ( ) ;
2019-05-06 19:48:48 +00:00
2018-11-07 17:30:59 +00:00
m_termination_status = status ;
m_termination_signal = 0 ;
2019-02-06 17:45:21 +00:00
die ( ) ;
2020-02-17 14:04:27 +00:00
Thread : : current - > die_if_needed ( ) ;
2018-11-07 21:15:02 +00:00
ASSERT_NOT_REACHED ( ) ;
2018-10-22 09:43:55 +00:00
}
2019-09-17 13:41:42 +00:00
void signal_trampoline_dummy ( void )
{
// The trampoline preserves the current eax, pushes the signal code and
// then calls the signal handler. We do this because, when interrupting a
// blocking syscall, that syscall may return some special error code in eax;
// This error code would likely be overwritten by the signal handler, so it's
// neccessary to preserve it here.
asm (
" .intel_syntax noprefix \n "
" asm_signal_trampoline: \n "
" push ebp \n "
" mov ebp, esp \n "
" push eax \n " // we have to store eax 'cause it might be the return value from a syscall
" sub esp, 4 \n " // align the stack to 16 bytes
" mov eax, [ebp+12] \n " // push the signal code
" push eax \n "
" call [ebp+8] \n " // call the signal handler
" add esp, 8 \n "
" mov eax, %P0 \n "
" int 0x82 \n " // sigreturn syscall
" asm_signal_trampoline_end: \n "
" .att_syntax " : : " i " ( Syscall : : SC_sigreturn ) ) ;
}
2019-09-04 13:14:54 +00:00
extern " C " void asm_signal_trampoline ( void ) ;
extern " C " void asm_signal_trampoline_end ( void ) ;
2019-07-19 15:01:16 +00:00
void create_signal_trampolines ( )
2019-03-05 11:50:55 +00:00
{
2019-07-19 15:01:16 +00:00
InterruptDisabler disabler ;
// NOTE: We leak this region.
2020-01-09 21:29:31 +00:00
auto * trampoline_region = MM . allocate_user_accessible_kernel_region ( PAGE_SIZE , " Signal trampolines " , Region : : Access : : Read | Region : : Access : : Write | Region : : Access : : Execute , false ) . leak_ptr ( ) ;
2019-07-19 15:01:16 +00:00
g_return_to_ring3_from_signal_trampoline = trampoline_region - > vaddr ( ) ;
2019-09-04 13:14:54 +00:00
u8 * trampoline = ( u8 * ) asm_signal_trampoline ;
u8 * trampoline_end = ( u8 * ) asm_signal_trampoline_end ;
size_t trampoline_size = trampoline_end - trampoline ;
2020-01-19 08:14:14 +00:00
{
SmapDisabler disabler ;
u8 * code_ptr = ( u8 * ) trampoline_region - > vaddr ( ) . as_ptr ( ) ;
memcpy ( code_ptr , trampoline , trampoline_size ) ;
}
2019-07-19 15:01:16 +00:00
trampoline_region - > set_writable ( false ) ;
2019-11-03 19:59:54 +00:00
trampoline_region - > remap ( ) ;
2019-03-05 11:50:55 +00:00
}
2019-12-15 20:29:26 +00:00
void create_kernel_info_page ( )
{
2019-12-25 21:41:34 +00:00
auto * info_page_region_for_userspace = MM . allocate_user_accessible_kernel_region ( PAGE_SIZE , " Kernel info page " , Region : : Access : : Read ) . leak_ptr ( ) ;
auto * info_page_region_for_kernel = MM . allocate_kernel_region_with_vmobject ( info_page_region_for_userspace - > vmobject ( ) , PAGE_SIZE , " Kernel info page " , Region : : Access : : Read | Region : : Access : : Write ) . leak_ptr ( ) ;
2019-12-15 21:21:28 +00:00
s_info_page_address_for_userspace = info_page_region_for_userspace - > vaddr ( ) ;
s_info_page_address_for_kernel = info_page_region_for_kernel - > vaddr ( ) ;
memset ( s_info_page_address_for_kernel . as_ptr ( ) , 0 , PAGE_SIZE ) ;
2019-12-15 20:29:26 +00:00
}
2020-02-15 23:15:37 +00:00
int Process : : sys $ sigreturn ( RegisterState & registers )
2018-11-07 20:19:47 +00:00
{
2020-01-12 09:32:56 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2019-09-04 13:14:54 +00:00
//Here, we restore the state pushed by dispatch signal and asm_signal_trampoline.
2020-01-09 17:02:01 +00:00
u32 * stack_ptr = ( u32 * ) registers . userspace_esp ;
2019-09-04 13:14:54 +00:00
u32 smuggled_eax = * stack_ptr ;
//pop the stored eax, ebp, return address, handler and signal code
stack_ptr + = 5 ;
2020-02-17 14:04:27 +00:00
Thread : : current - > m_signal_mask = * stack_ptr ;
2019-09-04 13:14:54 +00:00
stack_ptr + + ;
2019-10-04 14:31:34 +00:00
//pop edi, esi, ebp, esp, ebx, edx, ecx and eax
2020-03-08 09:36:51 +00:00
memcpy ( & registers . edi , stack_ptr , 8 * sizeof ( FlatPtr ) ) ;
2019-10-04 14:31:34 +00:00
stack_ptr + = 8 ;
registers . eip = * stack_ptr ;
stack_ptr + + ;
2019-09-04 13:14:54 +00:00
registers . eflags = * stack_ptr ;
stack_ptr + + ;
2020-01-09 17:02:01 +00:00
registers . userspace_esp = registers . esp ;
2019-09-04 13:14:54 +00:00
return smuggled_eax ;
2018-11-06 09:46:40 +00:00
}
2019-07-03 19:17:35 +00:00
void Process : : crash ( int signal , u32 eip )
2018-10-17 22:26:30 +00:00
{
2018-10-25 08:33:10 +00:00
ASSERT_INTERRUPTS_DISABLED ( ) ;
2019-03-23 21:03:17 +00:00
ASSERT ( ! is_dead ( ) ) ;
2020-02-17 14:04:27 +00:00
ASSERT ( Process : : current = = this ) ;
2019-03-13 22:14:30 +00:00
2020-04-08 11:30:50 +00:00
if ( eip > = 0xc0000000 & & g_kernel_symbols_available ) {
auto * symbol = symbolicate_kernel_address ( eip ) ;
dbg ( ) < < " \033 [31;1m " < < String : : format ( " %p " , eip ) < < " " < < ( symbol ? demangle ( symbol - > name ) : " (k?) " ) < < " + " < < ( symbol ? eip - symbol - > address : 0 ) < < " \033 [0m \n " ;
2020-03-02 09:40:40 +00:00
} else if ( auto elf_bundle = this - > elf_bundle ( ) ) {
dbg ( ) < < " \033 [31;1m " < < String : : format ( " %p " , eip ) < < " " < < elf_bundle - > elf_loader - > symbolicate ( eip ) < < " \033 [0m \n " ;
2020-01-18 13:38:39 +00:00
} else {
2020-02-24 22:13:33 +00:00
dbg ( ) < < " \033 [31;1m " < < String : : format ( " %p " , eip ) < < " (?) \033 [0m \n " ;
2020-01-18 13:38:39 +00:00
}
2019-05-16 17:49:48 +00:00
dump_backtrace ( ) ;
2019-05-26 00:08:51 +00:00
m_termination_signal = signal ;
2019-01-31 16:31:23 +00:00
dump_regions ( ) ;
2019-02-08 08:45:53 +00:00
ASSERT ( is_ring3 ( ) ) ;
2019-01-30 17:26:19 +00:00
die ( ) ;
Kernel: Unwind kernel stacks before dying
While executing in the kernel, a thread can acquire various resources
that need cleanup, such as locks and references to RefCounted objects.
This cleanup normally happens on the exit path, such as in destructors
for various RAII guards. But we weren't calling those exit paths when
killing threads that have been executing in the kernel, such as threads
blocked on reading or sleeping, thus causing leaks.
This commit changes how killing threads works. Now, instead of killing
a thread directly, one is supposed to call thread->set_should_die(),
which will unblock it and make it unwind the stack if it is blocked
in the kernel. Then, just before returning to the userspace, the thread
will automatically die.
2019-11-14 15:46:01 +00:00
// We can not return from here, as there is nowhere
// to unwind to, so die right away.
2020-02-17 14:04:27 +00:00
Thread : : current - > die_if_needed ( ) ;
2018-11-07 21:15:02 +00:00
ASSERT_NOT_REACHED ( ) ;
2018-10-17 22:26:30 +00:00
}
2018-11-07 20:38:18 +00:00
Process * Process : : from_pid ( pid_t pid )
2018-10-16 09:01:38 +00:00
{
2018-10-26 13:11:29 +00:00
ASSERT_INTERRUPTS_DISABLED ( ) ;
2019-08-08 12:40:13 +00:00
for ( auto & process : * g_processes ) {
if ( process . pid ( ) = = pid )
return & process ;
2018-10-16 09:01:38 +00:00
}
return nullptr ;
}
2020-01-07 14:53:42 +00:00
RefPtr < FileDescription > Process : : file_description ( int fd ) const
2018-11-07 10:37:54 +00:00
{
if ( fd < 0 )
return nullptr ;
2020-02-25 13:49:47 +00:00
if ( static_cast < size_t > ( fd ) < m_fds . size ( ) )
2019-06-13 20:03:04 +00:00
return m_fds [ fd ] . description . ptr ( ) ;
2018-10-16 09:01:38 +00:00
return nullptr ;
}
2019-09-28 20:00:38 +00:00
int Process : : fd_flags ( int fd ) const
{
if ( fd < 0 )
return - 1 ;
2020-02-25 13:49:47 +00:00
if ( static_cast < size_t > ( fd ) < m_fds . size ( ) )
2019-09-28 20:00:38 +00:00
return m_fds [ fd ] . flags ;
return - 1 ;
}
2019-02-25 20:19:57 +00:00
ssize_t Process : : sys $ get_dir_entries ( int fd , void * buffer , ssize_t size )
2018-10-24 10:43:52 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-02-25 20:19:57 +00:00
if ( size < 0 )
return - EINVAL ;
2018-11-16 15:10:59 +00:00
if ( ! validate_write ( buffer , size ) )
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-10-30 21:03:02 +00:00
return - EBADF ;
2019-07-03 19:17:35 +00:00
return description - > get_dir_entries ( ( u8 * ) buffer , size ) ;
2018-10-24 10:43:52 +00:00
}
2018-11-01 12:15:46 +00:00
int Process : : sys $ lseek ( int fd , off_t offset , int whence )
2018-10-16 09:01:38 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-10-31 16:50:43 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
return description - > seek ( offset , whence ) ;
2018-10-16 09:01:38 +00:00
}
2019-02-25 20:19:57 +00:00
int Process : : sys $ ttyname_r ( int fd , char * buffer , ssize_t size )
2018-10-30 21:03:02 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( tty ) ;
2019-02-25 20:19:57 +00:00
if ( size < 0 )
return - EINVAL ;
2018-11-16 15:10:59 +00:00
if ( ! validate_write ( buffer , size ) )
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-10-30 21:03:02 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_tty ( ) )
2018-10-30 21:03:02 +00:00
return - ENOTTY ;
2020-01-05 17:00:15 +00:00
String tty_name = description - > tty ( ) - > tty_name ( ) ;
2019-12-09 16:45:40 +00:00
if ( ( size_t ) size < tty_name . length ( ) + 1 )
2018-10-30 21:03:02 +00:00
return - ERANGE ;
2020-01-05 17:00:15 +00:00
copy_to_user ( buffer , tty_name . characters ( ) , tty_name . length ( ) + 1 ) ;
2018-10-30 21:03:02 +00:00
return 0 ;
}
2019-02-25 20:19:57 +00:00
int Process : : sys $ ptsname_r ( int fd , char * buffer , ssize_t size )
2019-01-15 05:30:19 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( tty ) ;
2019-02-25 20:19:57 +00:00
if ( size < 0 )
return - EINVAL ;
2019-01-15 05:30:19 +00:00
if ( ! validate_write ( buffer , size ) )
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-01-15 05:30:19 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
auto * master_pty = description - > master_pty ( ) ;
2019-01-15 05:30:19 +00:00
if ( ! master_pty )
return - ENOTTY ;
auto pts_name = master_pty - > pts_name ( ) ;
2019-12-09 16:45:40 +00:00
if ( ( size_t ) size < pts_name . length ( ) + 1 )
2019-01-15 05:30:19 +00:00
return - ERANGE ;
2020-01-05 17:00:15 +00:00
copy_to_user ( buffer , pts_name . characters ( ) , pts_name . length ( ) + 1 ) ;
2019-01-15 05:30:19 +00:00
return 0 ;
}
2019-05-10 01:19:25 +00:00
ssize_t Process : : sys $ writev ( int fd , const struct iovec * iov , int iov_count )
2018-10-30 14:33:37 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-05-10 01:19:25 +00:00
if ( iov_count < 0 )
2019-02-25 20:19:57 +00:00
return - EINVAL ;
2019-05-10 01:19:25 +00:00
if ( ! validate_read_typed ( iov , iov_count ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2019-05-10 01:19:25 +00:00
2020-01-02 12:01:41 +00:00
u64 total_length = 0 ;
2020-01-05 09:16:19 +00:00
Vector < iovec , 32 > vecs ;
2020-01-26 09:10:15 +00:00
vecs . resize ( iov_count ) ;
2020-01-05 18:20:08 +00:00
copy_from_user ( vecs . data ( ) , iov , iov_count * sizeof ( iovec ) ) ;
for ( auto & vec : vecs ) {
if ( ! validate_read ( vec . iov_base , vec . iov_len ) )
2020-01-05 09:16:19 +00:00
return - EFAULT ;
2020-01-05 18:20:08 +00:00
total_length + = vec . iov_len ;
2020-01-02 12:01:41 +00:00
if ( total_length > INT32_MAX )
return - EINVAL ;
}
2019-05-10 01:19:25 +00:00
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-10-30 14:33:37 +00:00
return - EBADF ;
2019-05-10 01:19:25 +00:00
2020-01-03 02:29:59 +00:00
if ( ! description - > is_writable ( ) )
return - EBADF ;
2019-05-10 01:19:25 +00:00
int nwritten = 0 ;
2020-01-05 09:16:19 +00:00
for ( auto & vec : vecs ) {
int rc = do_write ( * description , ( const u8 * ) vec . iov_base , vec . iov_len ) ;
2019-05-10 01:19:25 +00:00
if ( rc < 0 ) {
if ( nwritten = = 0 )
return rc ;
return nwritten ;
}
nwritten + = rc ;
}
return nwritten ;
}
2019-07-03 19:17:35 +00:00
ssize_t Process : : do_write ( FileDescription & description , const u8 * data , int data_size )
2019-05-10 01:19:25 +00:00
{
2018-11-12 00:28:46 +00:00
ssize_t nwritten = 0 ;
2019-06-13 20:03:04 +00:00
if ( ! description . is_blocking ( ) ) {
if ( ! description . can_write ( ) )
2019-05-19 08:25:05 +00:00
return - EAGAIN ;
}
2019-05-10 01:19:25 +00:00
2019-06-13 20:03:04 +00:00
if ( description . should_append ( ) ) {
2019-05-25 23:18:04 +00:00
# ifdef IO_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " seeking to end (O_APPEND) " ;
2019-05-25 23:18:04 +00:00
# endif
2019-06-13 20:03:04 +00:00
description . seek ( 0 , SEEK_END ) ;
2019-05-25 23:18:04 +00:00
}
2019-05-10 01:19:25 +00:00
while ( nwritten < data_size ) {
2018-11-12 00:28:46 +00:00
# ifdef IO_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " while " < < nwritten < < " < " < < size ;
2018-11-12 00:28:46 +00:00
# endif
2019-06-13 20:03:04 +00:00
if ( ! description . can_write ( ) ) {
2018-11-12 00:28:46 +00:00
# ifdef IO_DEBUG
2020-02-29 11:51:44 +00:00
dbg ( ) < < " block write on " < < description . absolute_path ( ) ;
2018-11-12 00:28:46 +00:00
# endif
2020-02-17 14:04:27 +00:00
if ( Thread : : current - > block < Thread : : WriteBlocker > ( description ) ! = Thread : : BlockResult : : WokeNormally ) {
2019-07-20 09:05:52 +00:00
if ( nwritten = = 0 )
return - EINTR ;
}
2019-05-10 01:19:25 +00:00
}
2019-06-13 20:03:04 +00:00
ssize_t rc = description . write ( data + nwritten , data_size - nwritten ) ;
2018-11-12 00:28:46 +00:00
# ifdef IO_DEBUG
2020-02-24 22:13:33 +00:00
dbg ( ) < < " -> write returned " < < rc ;
2018-11-12 00:28:46 +00:00
# endif
2019-05-10 01:19:25 +00:00
if ( rc < 0 ) {
2020-02-29 17:42:35 +00:00
if ( nwritten )
return nwritten ;
2019-05-10 01:19:25 +00:00
return rc ;
2018-11-12 00:28:46 +00:00
}
2019-05-10 01:19:25 +00:00
if ( rc = = 0 )
break ;
nwritten + = rc ;
2018-11-12 00:28:46 +00:00
}
2019-05-10 01:19:25 +00:00
return nwritten ;
}
2019-07-03 19:17:35 +00:00
ssize_t Process : : sys $ write ( int fd , const u8 * data , ssize_t size )
2019-05-10 01:19:25 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-05-10 01:19:25 +00:00
if ( size < 0 )
return - EINVAL ;
if ( size = = 0 )
return 0 ;
if ( ! validate_read ( data , size ) )
return - EFAULT ;
# ifdef DEBUG_IO
2020-02-29 11:51:44 +00:00
dbg ( ) < < " sys$write( " < < fd < < " , " < < ( const void * ) ( data ) < < " , " < < size < < " ) " ;
2019-05-10 01:19:25 +00:00
# endif
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-05-10 01:19:25 +00:00
return - EBADF ;
2020-01-03 02:29:59 +00:00
if ( ! description - > is_writable ( ) )
return - EBADF ;
2019-11-19 12:33:08 +00:00
return do_write ( * description , data , size ) ;
2018-10-30 14:33:37 +00:00
}
2019-07-03 19:17:35 +00:00
ssize_t Process : : sys $ read ( int fd , u8 * buffer , ssize_t size )
2018-10-16 09:01:38 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-02-25 20:19:57 +00:00
if ( size < 0 )
return - EINVAL ;
2019-04-27 14:17:24 +00:00
if ( size = = 0 )
return 0 ;
2019-02-25 20:19:57 +00:00
if ( ! validate_write ( buffer , size ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2018-10-23 08:12:50 +00:00
# ifdef DEBUG_IO
2020-02-29 11:51:44 +00:00
dbg ( ) < < " sys$read( " < < fd < < " , " < < ( const void * ) buffer < < " , " < < size < < " ) " ;
2018-10-23 08:12:50 +00:00
# endif
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-10-30 14:33:37 +00:00
return - EBADF ;
2020-01-03 02:29:59 +00:00
if ( ! description - > is_readable ( ) )
return - EBADF ;
2019-10-24 13:46:31 +00:00
if ( description - > is_directory ( ) )
return - EISDIR ;
2019-06-13 20:03:04 +00:00
if ( description - > is_blocking ( ) ) {
if ( ! description - > can_read ( ) ) {
2020-02-17 14:04:27 +00:00
if ( Thread : : current - > block < Thread : : ReadBlocker > ( * description ) ! = Thread : : BlockResult : : WokeNormally )
2018-11-07 20:19:47 +00:00
return - EINTR ;
2020-01-26 16:54:23 +00:00
if ( ! description - > can_read ( ) )
return - EAGAIN ;
2018-10-25 11:07:59 +00:00
}
}
2019-06-13 20:03:04 +00:00
return description - > read ( buffer , size ) ;
2018-10-16 09:01:38 +00:00
}
2018-11-01 12:15:46 +00:00
int Process : : sys $ close ( int fd )
2018-10-16 09:01:38 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-12-02 08:33:37 +00:00
# ifdef DEBUG_IO
2020-02-29 11:51:44 +00:00
dbg ( ) < < " sys$close( " < < fd < < " ) " < < description . ptr ( ) ;
2019-12-02 08:33:37 +00:00
# endif
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-10-30 21:03:02 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
int rc = description - > close ( ) ;
2019-06-07 09:43:58 +00:00
m_fds [ fd ] = { } ;
2018-11-01 13:00:28 +00:00
return rc ;
2018-10-16 09:01:38 +00:00
}
2020-01-06 11:23:30 +00:00
int Process : : sys $ utime ( const char * user_path , size_t path_length , const utimbuf * user_buf )
2018-12-19 20:14:55 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( fattr ) ;
2020-01-06 11:23:30 +00:00
if ( user_buf & & ! validate_read_typed ( user_buf ) )
2018-12-19 20:14:55 +00:00
return - EFAULT ;
2020-01-06 11:23:30 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
utimbuf buf ;
if ( user_buf ) {
2020-01-13 07:00:35 +00:00
copy_from_user ( & buf , user_buf ) ;
2018-12-19 20:14:55 +00:00
} else {
2020-01-06 11:23:30 +00:00
auto now = kgettimeofday ( ) ;
buf = { now . tv_sec , now . tv_sec } ;
2018-12-19 20:14:55 +00:00
}
2020-01-06 11:23:30 +00:00
return VFS : : the ( ) . utime ( path . value ( ) , current_directory ( ) , buf . actime , buf . modtime ) ;
2018-12-19 20:14:55 +00:00
}
2020-01-06 09:43:53 +00:00
int Process : : sys $ access ( const char * user_path , size_t path_length , int mode )
2018-11-10 23:20:53 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2020-01-06 10:10:38 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . access ( path . value ( ) , mode , current_directory ( ) ) ;
2018-11-10 23:20:53 +00:00
}
2019-07-03 19:17:35 +00:00
int Process : : sys $ fcntl ( int fd , int cmd , u32 arg )
2018-11-11 09:38:33 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-12 08:59:04 +00:00
# ifdef DEBUG_IO
2020-02-24 22:13:33 +00:00
dbg ( ) < < " sys$fcntl: fd= " < < fd < < " , cmd= " < < cmd < < " , arg= " < < arg ;
2020-01-12 08:59:04 +00:00
# endif
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-11-11 09:38:33 +00:00
return - EBADF ;
2019-06-07 07:36:51 +00:00
// NOTE: The FD flags are not shared between FileDescription objects.
2018-11-13 00:36:31 +00:00
// This means that dup() doesn't copy the FD_CLOEXEC flag!
2018-11-11 14:36:40 +00:00
switch ( cmd ) {
2018-11-16 21:14:40 +00:00
case F_DUPFD : {
int arg_fd = ( int ) arg ;
if ( arg_fd < 0 )
return - EINVAL ;
2019-04-06 12:54:32 +00:00
int new_fd = alloc_fd ( arg_fd ) ;
if ( new_fd < 0 )
return new_fd ;
2019-06-13 20:03:04 +00:00
m_fds [ new_fd ] . set ( * description ) ;
2020-01-20 12:15:24 +00:00
return new_fd ;
2018-11-16 21:14:40 +00:00
}
2018-11-11 14:36:40 +00:00
case F_GETFD :
2018-11-13 00:36:31 +00:00
return m_fds [ fd ] . flags ;
2018-11-11 14:36:40 +00:00
case F_SETFD :
2018-11-13 00:36:31 +00:00
m_fds [ fd ] . flags = arg ;
break ;
2018-11-11 14:36:40 +00:00
case F_GETFL :
2019-06-13 20:03:04 +00:00
return description - > file_flags ( ) ;
2018-11-11 14:36:40 +00:00
case F_SETFL :
2019-06-13 20:03:04 +00:00
description - > set_file_flags ( arg ) ;
2018-11-13 00:36:31 +00:00
break ;
2018-11-11 14:36:40 +00:00
default :
ASSERT_NOT_REACHED ( ) ;
}
return 0 ;
2018-11-11 09:38:33 +00:00
}
2020-03-10 12:34:24 +00:00
int Process : : sys $ fstat ( int fd , stat * user_statbuf )
2018-11-10 23:20:53 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-03-10 12:34:24 +00:00
if ( ! validate_write_typed ( user_statbuf ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-11-10 23:20:53 +00:00
return - EBADF ;
2020-03-10 12:34:24 +00:00
stat buffer ;
memset ( & buffer , 0 , sizeof ( buffer ) ) ;
int rc = description - > fstat ( buffer ) ;
copy_to_user ( user_statbuf , & buffer ) ;
return rc ;
2018-11-10 23:20:53 +00:00
}
2020-02-10 18:32:40 +00:00
int Process : : sys $ stat ( const Syscall : : SC_stat_params * user_params )
2018-10-24 11:19:36 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2020-02-10 18:32:40 +00:00
Syscall : : SC_stat_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-02-10 18:32:40 +00:00
if ( ! validate_write_typed ( params . statbuf ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-02-10 18:32:40 +00:00
auto path = get_syscall_path_argument ( params . path ) ;
2020-01-06 10:10:38 +00:00
if ( path . is_error ( ) )
return path . error ( ) ;
2020-02-10 18:32:40 +00:00
auto metadata_or_error = VFS : : the ( ) . lookup_metadata ( path . value ( ) , current_directory ( ) , params . follow_symlinks ? 0 : O_NOFOLLOW_NOERROR ) ;
2019-08-02 17:23:23 +00:00
if ( metadata_or_error . is_error ( ) )
return metadata_or_error . error ( ) ;
2020-01-06 11:13:48 +00:00
stat statbuf ;
auto result = metadata_or_error . value ( ) . stat ( statbuf ) ;
if ( result . is_error ( ) )
return result ;
2020-02-10 18:32:40 +00:00
copy_to_user ( params . statbuf , & statbuf ) ;
2020-01-06 11:13:48 +00:00
return 0 ;
2018-10-31 09:14:56 +00:00
}
2020-01-10 19:13:23 +00:00
template < typename DataType , typename SizeType >
bool Process : : validate ( const Syscall : : MutableBufferArgument < DataType , SizeType > & buffer )
2018-10-28 13:11:51 +00:00
{
2020-01-10 19:13:23 +00:00
return validate_write ( buffer . data , buffer . size ) ;
}
2020-01-11 11:47:47 +00:00
template < typename DataType , typename SizeType >
bool Process : : validate ( const Syscall : : ImmutableBufferArgument < DataType , SizeType > & buffer )
{
return validate_read ( buffer . data , buffer . size ) ;
}
String Process : : validate_and_copy_string_from_user ( const char * user_characters , size_t user_length ) const
2020-01-10 20:26:47 +00:00
{
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
if ( user_length = = 0 )
return String : : empty ( ) ;
2020-01-25 13:14:11 +00:00
if ( ! user_characters )
return { } ;
2020-01-11 11:47:47 +00:00
if ( ! validate_read ( user_characters , user_length ) )
2020-01-10 20:26:47 +00:00
return { } ;
SmapDisabler disabler ;
2020-01-11 11:47:47 +00:00
size_t measured_length = strnlen ( user_characters , user_length ) ;
return String ( user_characters , measured_length ) ;
}
String Process : : validate_and_copy_string_from_user ( const Syscall : : StringArgument & string ) const
{
return validate_and_copy_string_from_user ( string . characters , string . length ) ;
2020-01-10 20:26:47 +00:00
}
2020-01-10 19:13:23 +00:00
int Process : : sys $ readlink ( const Syscall : : SC_readlink_params * user_params )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2020-01-13 10:19:18 +00:00
2020-01-10 19:13:23 +00:00
Syscall : : SC_readlink_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-10 19:13:23 +00:00
if ( ! validate ( params . buffer ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2018-10-28 13:11:51 +00:00
2020-01-11 11:47:47 +00:00
auto path = get_syscall_path_argument ( params . path ) ;
2020-01-10 19:13:23 +00:00
if ( path . is_error ( ) )
return path . error ( ) ;
auto result = VFS : : the ( ) . open ( path . value ( ) , O_RDONLY | O_NOFOLLOW_NOERROR , 0 , current_directory ( ) ) ;
2019-03-06 21:14:31 +00:00
if ( result . is_error ( ) )
return result . error ( ) ;
2019-06-13 20:03:04 +00:00
auto description = result . value ( ) ;
2018-10-28 13:11:51 +00:00
2019-06-13 20:03:04 +00:00
if ( ! description - > metadata ( ) . is_symlink ( ) )
2018-10-28 13:11:51 +00:00
return - EINVAL ;
2019-06-13 20:03:04 +00:00
auto contents = description - > read_entire_file ( ) ;
2018-10-28 13:11:51 +00:00
if ( ! contents )
return - EIO ; // FIXME: Get a more detailed error from VFS.
2020-01-10 19:13:23 +00:00
auto link_target = String : : copy ( contents ) ;
if ( link_target . length ( ) + 1 > params . buffer . size )
return - ENAMETOOLONG ;
copy_to_user ( params . buffer . data , link_target . characters ( ) , link_target . length ( ) + 1 ) ;
2020-01-27 20:47:30 +00:00
return link_target . length ( ) + 1 ;
2018-10-28 13:11:51 +00:00
}
2020-01-05 21:06:25 +00:00
int Process : : sys $ chdir ( const char * user_path , size_t path_length )
2018-10-24 12:28:22 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2020-01-06 10:10:38 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
auto directory_or_error = VFS : : the ( ) . open_directory ( path . value ( ) , current_directory ( ) ) ;
2019-03-01 22:54:07 +00:00
if ( directory_or_error . is_error ( ) )
return directory_or_error . error ( ) ;
2019-05-30 18:23:50 +00:00
m_cwd = * directory_or_error . value ( ) ;
2018-10-24 12:28:22 +00:00
return 0 ;
}
2019-09-11 23:18:25 +00:00
int Process : : sys $ fchdir ( int fd )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-09-11 23:18:25 +00:00
if ( ! description )
return - EBADF ;
if ( ! description - > is_directory ( ) )
2019-10-20 14:24:42 +00:00
return - ENOTDIR ;
2019-09-11 23:18:25 +00:00
2019-09-13 12:35:18 +00:00
if ( ! description - > metadata ( ) . may_execute ( * this ) )
return - EACCES ;
2019-09-11 23:18:25 +00:00
m_cwd = description - > custody ( ) ;
return 0 ;
}
2019-02-25 20:19:57 +00:00
int Process : : sys $ getcwd ( char * buffer , ssize_t size )
2018-10-26 12:24:11 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2019-02-25 20:19:57 +00:00
if ( size < 0 )
return - EINVAL ;
2018-11-16 15:10:59 +00:00
if ( ! validate_write ( buffer , size ) )
return - EFAULT ;
2019-05-30 18:23:50 +00:00
auto path = current_directory ( ) . absolute_path ( ) ;
2019-12-09 16:45:40 +00:00
if ( ( size_t ) size < path . length ( ) + 1 )
2018-10-29 23:06:31 +00:00
return - ERANGE ;
2020-01-05 17:00:15 +00:00
copy_to_user ( buffer , path . characters ( ) , path . length ( ) + 1 ) ;
2018-11-11 14:36:40 +00:00
return 0 ;
2018-10-26 12:24:11 +00:00
}
2019-03-06 21:30:13 +00:00
int Process : : number_of_open_file_descriptors ( ) const
2018-11-01 12:39:28 +00:00
{
2019-03-06 21:30:13 +00:00
int count = 0 ;
2019-06-13 20:03:04 +00:00
for ( auto & description : m_fds ) {
if ( description )
2018-11-01 12:39:28 +00:00
+ + count ;
}
return count ;
}
2020-01-05 20:51:06 +00:00
int Process : : sys $ open ( const Syscall : : SC_open_params * user_params )
2018-10-16 09:01:38 +00:00
{
2020-01-13 10:19:18 +00:00
Syscall : : SC_open_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
2019-07-08 18:01:49 +00:00
return - EFAULT ;
2020-01-05 17:00:15 +00:00
2020-01-05 20:51:06 +00:00
int dirfd = params . dirfd ;
int options = params . options ;
u16 mode = params . mode ;
2020-01-05 08:13:00 +00:00
2020-01-15 20:19:26 +00:00
if ( options & O_NOFOLLOW_NOERROR )
return - EINVAL ;
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
if ( options & O_UNLINK_INTERNAL )
return - EINVAL ;
2020-01-21 12:14:26 +00:00
if ( options & O_WRONLY )
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( wpath ) ;
2020-01-21 12:14:26 +00:00
else if ( options & O_RDONLY )
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
if ( options & O_CREAT )
REQUIRE_PROMISE ( cpath ) ;
2020-01-08 12:59:22 +00:00
// Ignore everything except permission bits.
mode & = 04777 ;
2020-01-11 11:47:47 +00:00
auto path = get_syscall_path_argument ( params . path ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
2019-11-10 12:47:02 +00:00
# ifdef DEBUG_IO
2020-02-21 11:25:40 +00:00
dbg ( ) < < " sys$open(dirfd= " < < dirfd < < " , path= \" " < < path . value ( ) < < " \" , options= " < < options < < " , mode= " < < mode < < " ) " ;
2019-11-10 12:47:02 +00:00
# endif
2019-04-06 12:54:32 +00:00
int fd = alloc_fd ( ) ;
if ( fd < 0 )
return fd ;
2019-11-10 12:47:02 +00:00
RefPtr < Custody > base ;
if ( dirfd = = AT_FDCWD ) {
base = current_directory ( ) ;
} else {
2020-01-07 14:53:42 +00:00
auto base_description = file_description ( dirfd ) ;
2019-11-10 12:47:02 +00:00
if ( ! base_description )
return - EBADF ;
if ( ! base_description - > is_directory ( ) )
return - ENOTDIR ;
if ( ! base_description - > custody ( ) )
return - EINVAL ;
base = base_description - > custody ( ) ;
}
2020-01-11 11:47:47 +00:00
auto result = VFS : : the ( ) . open ( path . value ( ) , options , mode & ~ umask ( ) , * base ) ;
2019-03-06 21:14:31 +00:00
if ( result . is_error ( ) )
return result . error ( ) ;
2019-06-13 20:03:04 +00:00
auto description = result . value ( ) ;
2019-07-03 19:17:35 +00:00
u32 fd_flags = ( options & O_CLOEXEC ) ? FD_CLOEXEC : 0 ;
2019-06-13 20:03:04 +00:00
m_fds [ fd ] . set ( move ( description ) , fd_flags ) ;
2018-10-26 12:30:13 +00:00
return fd ;
2018-10-16 09:01:38 +00:00
}
2019-04-06 12:54:32 +00:00
int Process : : alloc_fd ( int first_candidate_fd )
2018-11-12 00:28:46 +00:00
{
2019-04-06 12:54:32 +00:00
for ( int i = first_candidate_fd ; i < ( int ) m_max_open_file_descriptors ; + + i ) {
2020-01-20 12:18:54 +00:00
if ( ! m_fds [ i ] )
return i ;
2018-11-12 00:28:46 +00:00
}
2020-01-20 12:18:54 +00:00
return - EMFILE ;
2018-11-12 00:28:46 +00:00
}
2019-08-05 12:29:05 +00:00
int Process : : sys $ pipe ( int pipefd [ 2 ] , int flags )
2018-11-10 23:20:53 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-16 15:23:39 +00:00
if ( ! validate_write_typed ( pipefd ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2018-11-12 00:28:46 +00:00
if ( number_of_open_file_descriptors ( ) + 2 > max_open_file_descriptors ( ) )
return - EMFILE ;
2019-08-05 12:29:05 +00:00
// Reject flags other than O_CLOEXEC.
if ( ( flags & O_CLOEXEC ) ! = flags )
return - EINVAL ;
u32 fd_flags = ( flags & O_CLOEXEC ) ? FD_CLOEXEC : 0 ;
2019-04-25 11:52:07 +00:00
auto fifo = FIFO : : create ( m_uid ) ;
2018-11-12 00:28:46 +00:00
int reader_fd = alloc_fd ( ) ;
2019-08-05 12:29:05 +00:00
m_fds [ reader_fd ] . set ( fifo - > open_direction ( FIFO : : Direction : : Reader ) , fd_flags ) ;
2020-01-03 02:29:59 +00:00
m_fds [ reader_fd ] . description - > set_readable ( true ) ;
2020-01-20 12:38:31 +00:00
copy_to_user ( & pipefd [ 0 ] , & reader_fd ) ;
2018-11-12 00:28:46 +00:00
int writer_fd = alloc_fd ( ) ;
2019-08-05 12:29:05 +00:00
m_fds [ writer_fd ] . set ( fifo - > open_direction ( FIFO : : Direction : : Writer ) , fd_flags ) ;
2020-01-03 02:29:59 +00:00
m_fds [ writer_fd ] . description - > set_writable ( true ) ;
2020-01-20 12:38:31 +00:00
copy_to_user ( & pipefd [ 1 ] , & writer_fd ) ;
2018-11-12 00:28:46 +00:00
return 0 ;
2018-11-10 23:20:53 +00:00
}
int Process : : sys $ killpg ( int pgrp , int signum )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2018-11-10 23:20:53 +00:00
if ( signum < 1 | | signum > = 32 )
return - EINVAL ;
2019-11-14 16:16:30 +00:00
if ( pgrp < 0 )
return - EINVAL ;
return do_killpg ( pgrp , signum ) ;
2018-11-10 23:20:53 +00:00
}
2019-02-21 22:35:07 +00:00
int Process : : sys $ setuid ( uid_t uid )
2018-11-10 23:20:53 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( id ) ;
2019-02-21 22:35:07 +00:00
if ( uid ! = m_uid & & ! is_superuser ( ) )
return - EPERM ;
m_uid = uid ;
m_euid = uid ;
return 0 ;
2018-11-10 23:20:53 +00:00
}
2019-02-21 22:35:07 +00:00
int Process : : sys $ setgid ( gid_t gid )
2018-11-10 23:20:53 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( id ) ;
2019-02-21 22:35:07 +00:00
if ( gid ! = m_gid & & ! is_superuser ( ) )
return - EPERM ;
m_gid = gid ;
m_egid = gid ;
return 0 ;
2018-11-10 23:20:53 +00:00
}
unsigned Process : : sys $ alarm ( unsigned seconds )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-06-07 09:30:07 +00:00
unsigned previous_alarm_remaining = 0 ;
if ( m_alarm_deadline & & m_alarm_deadline > g_uptime ) {
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
previous_alarm_remaining = ( m_alarm_deadline - g_uptime ) / TimeManagement : : the ( ) . ticks_per_second ( ) ;
2019-06-07 09:30:07 +00:00
}
if ( ! seconds ) {
m_alarm_deadline = 0 ;
return previous_alarm_remaining ;
}
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
m_alarm_deadline = g_uptime + seconds * TimeManagement : : the ( ) . ticks_per_second ( ) ;
2019-06-07 09:30:07 +00:00
return previous_alarm_remaining ;
2018-11-10 23:20:53 +00:00
}
2018-11-01 12:15:46 +00:00
int Process : : sys $ uname ( utsname * buf )
2018-10-26 12:56:21 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-16 15:23:39 +00:00
if ( ! validate_write_typed ( buf ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2019-02-07 09:29:26 +00:00
LOCKER ( * s_hostname_lock ) ;
2020-01-05 17:00:15 +00:00
if ( s_hostname - > length ( ) + 1 > sizeof ( utsname : : nodename ) )
return - ENAMETOOLONG ;
copy_to_user ( buf - > sysname , " SerenityOS " , 11 ) ;
copy_to_user ( buf - > release , " 1.0-dev " , 8 ) ;
copy_to_user ( buf - > version , " FIXME " , 6 ) ;
copy_to_user ( buf - > machine , " i686 " , 5 ) ;
copy_to_user ( buf - > nodename , s_hostname - > characters ( ) , s_hostname - > length ( ) + 1 ) ;
2018-10-26 12:56:21 +00:00
return 0 ;
}
2019-11-14 16:16:30 +00:00
KResult Process : : do_kill ( Process & process , int signal )
{
// FIXME: Allow sending SIGCONT to everyone in the process group.
// FIXME: Should setuid processes have some special treatment here?
if ( ! is_superuser ( ) & & m_euid ! = process . m_uid & & m_uid ! = process . m_uid )
return KResult ( - EPERM ) ;
if ( process . is_ring0 ( ) & & signal = = SIGKILL ) {
2020-03-01 19:45:39 +00:00
klog ( ) < < " attempted to send SIGKILL to ring 0 process " < < process . name ( ) . characters ( ) < < " ( " < < process . pid ( ) < < " ) " ;
2019-11-14 16:16:30 +00:00
return KResult ( - EPERM ) ;
}
2020-01-03 01:01:45 +00:00
if ( signal ! = 0 )
process . send_signal ( signal , this ) ;
2019-11-14 16:16:30 +00:00
return KSuccess ;
}
KResult Process : : do_killpg ( pid_t pgrp , int signal )
{
2020-02-27 10:05:16 +00:00
InterruptDisabler disabler ;
2019-11-14 16:16:30 +00:00
ASSERT ( pgrp > = 0 ) ;
// Send the signal to all processes in the given group.
if ( pgrp = = 0 ) {
// Send the signal to our own pgrp.
pgrp = pgid ( ) ;
}
bool group_was_empty = true ;
bool any_succeeded = false ;
KResult error = KSuccess ;
Process : : for_each_in_pgrp ( pgrp , [ & ] ( auto & process ) {
group_was_empty = false ;
KResult res = do_kill ( process , signal ) ;
if ( res . is_success ( ) )
any_succeeded = true ;
else
error = res ;
return IterationDecision : : Continue ;
} ) ;
if ( group_was_empty )
return KResult ( - ESRCH ) ;
if ( any_succeeded )
return KSuccess ;
return error ;
}
2018-11-02 13:06:48 +00:00
int Process : : sys $ kill ( pid_t pid , int signal )
2018-10-16 09:01:38 +00:00
{
2020-01-19 07:50:55 +00:00
if ( pid = = m_pid )
REQUIRE_PROMISE ( stdio ) ;
else
REQUIRE_PROMISE ( proc ) ;
2019-02-28 10:45:45 +00:00
if ( signal < 0 | | signal > = 32 )
return - EINVAL ;
2020-02-27 08:58:53 +00:00
if ( pid < = 0 ) {
if ( pid = = INT32_MIN )
return - EINVAL ;
2019-11-14 16:16:30 +00:00
return do_killpg ( - pid , signal ) ;
2020-02-27 08:58:53 +00:00
}
2018-10-16 09:01:38 +00:00
if ( pid = = - 1 ) {
// FIXME: Send to all processes.
2020-01-03 01:01:45 +00:00
return - ENOTIMPL ;
2018-10-16 09:01:38 +00:00
}
2019-02-28 08:44:48 +00:00
if ( pid = = m_pid ) {
2020-01-03 01:01:45 +00:00
if ( signal = = 0 )
return 0 ;
2020-02-17 14:04:27 +00:00
if ( ! Thread : : current - > should_ignore_signal ( signal ) ) {
Thread : : current - > send_signal ( signal , this ) ;
( void ) Thread : : current - > block < Thread : : SemiPermanentBlocker > ( Thread : : SemiPermanentBlocker : : Reason : : Signal ) ;
2020-01-03 01:01:45 +00:00
}
2019-02-28 08:44:48 +00:00
return 0 ;
}
2019-02-28 10:45:45 +00:00
InterruptDisabler disabler ;
auto * peer = Process : : from_pid ( pid ) ;
2018-10-31 00:06:57 +00:00
if ( ! peer )
return - ESRCH ;
2019-11-14 16:16:30 +00:00
return do_kill ( * peer , signal ) ;
2018-10-16 09:01:38 +00:00
}
2019-02-03 15:11:28 +00:00
int Process : : sys $ usleep ( useconds_t usec )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-02-03 15:11:28 +00:00
if ( ! usec )
return 0 ;
2020-02-17 14:04:27 +00:00
u64 wakeup_time = Thread : : current - > sleep ( usec / 1000 ) ;
2019-11-06 20:01:44 +00:00
if ( wakeup_time > g_uptime )
return - EINTR ;
2019-02-03 15:11:28 +00:00
return 0 ;
}
2018-11-01 12:15:46 +00:00
int Process : : sys $ sleep ( unsigned seconds )
2018-10-25 11:53:49 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-10-25 11:53:49 +00:00
if ( ! seconds )
return 0 ;
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
u64 wakeup_time = Thread : : current - > sleep ( seconds * TimeManagement : : the ( ) . ticks_per_second ( ) ) ;
2019-07-18 15:26:11 +00:00
if ( wakeup_time > g_uptime ) {
u32 ticks_left_until_original_wakeup_time = wakeup_time - g_uptime ;
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
return ticks_left_until_original_wakeup_time / TimeManagement : : the ( ) . ticks_per_second ( ) ;
2018-11-07 20:19:47 +00:00
}
2018-10-25 11:53:49 +00:00
return 0 ;
}
2019-06-06 15:46:41 +00:00
timeval kgettimeofday ( )
2019-03-13 12:13:23 +00:00
{
2019-12-15 21:21:28 +00:00
return const_cast < const timeval & > ( ( ( KernelInfoPage * ) s_info_page_address_for_kernel . as_ptr ( ) ) - > now ) ;
2019-06-06 15:46:41 +00:00
}
void kgettimeofday ( timeval & tv )
{
tv = kgettimeofday ( ) ;
2019-03-13 12:13:23 +00:00
}
2018-11-01 12:15:46 +00:00
int Process : : sys $ gettimeofday ( timeval * tv )
2018-10-25 15:29:49 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-16 15:23:39 +00:00
if ( ! validate_write_typed ( tv ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2019-12-15 20:29:26 +00:00
* tv = kgettimeofday ( ) ;
2018-10-25 15:29:49 +00:00
return 0 ;
}
2018-11-01 12:15:46 +00:00
uid_t Process : : sys $ getuid ( )
2018-10-16 09:01:38 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-10-16 09:01:38 +00:00
return m_uid ;
}
2018-11-01 12:15:46 +00:00
gid_t Process : : sys $ getgid ( )
2018-10-22 11:55:11 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-10-22 11:55:11 +00:00
return m_gid ;
}
2018-11-05 14:04:19 +00:00
uid_t Process : : sys $ geteuid ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-05 14:04:19 +00:00
return m_euid ;
}
gid_t Process : : sys $ getegid ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-05 14:04:19 +00:00
return m_egid ;
}
2018-11-01 12:15:46 +00:00
pid_t Process : : sys $ getpid ( )
2018-10-22 11:55:11 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-10-22 11:55:11 +00:00
return m_pid ;
}
2018-11-06 12:33:06 +00:00
pid_t Process : : sys $ getppid ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-06 12:33:06 +00:00
return m_ppid ;
}
2018-11-06 12:40:23 +00:00
mode_t Process : : sys $ umask ( mode_t mask )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-06 12:40:23 +00:00
auto old_mask = m_umask ;
2019-02-22 01:39:13 +00:00
m_umask = mask & 0777 ;
2018-11-06 12:40:23 +00:00
return old_mask ;
}
2020-02-05 16:42:43 +00:00
siginfo_t Process : : reap ( Process & process )
2018-11-07 22:59:49 +00:00
{
2020-02-05 16:42:43 +00:00
siginfo_t siginfo ;
2020-02-06 13:12:20 +00:00
memset ( & siginfo , 0 , sizeof ( siginfo ) ) ;
2020-02-05 16:42:43 +00:00
siginfo . si_signo = SIGCHLD ;
siginfo . si_pid = process . pid ( ) ;
siginfo . si_uid = process . uid ( ) ;
if ( process . m_termination_signal ) {
siginfo . si_status = process . m_termination_signal ;
siginfo . si_code = CLD_KILLED ;
} else {
siginfo . si_status = process . m_termination_status ;
siginfo . si_code = CLD_EXITED ;
}
2019-03-27 13:38:32 +00:00
{
InterruptDisabler disabler ;
2018-12-03 00:12:26 +00:00
2019-03-27 13:38:32 +00:00
if ( process . ppid ( ) ) {
auto * parent = Process : : from_pid ( process . ppid ( ) ) ;
if ( parent ) {
parent - > m_ticks_in_user_for_dead_children + = process . m_ticks_in_user + process . m_ticks_in_user_for_dead_children ;
parent - > m_ticks_in_kernel_for_dead_children + = process . m_ticks_in_kernel + process . m_ticks_in_kernel_for_dead_children ;
}
2019-01-30 18:35:38 +00:00
}
2018-12-03 00:12:26 +00:00
2020-02-01 09:27:25 +00:00
# ifdef PROCESS_DEBUG
2020-01-21 15:14:39 +00:00
dbg ( ) < < " Reaping process " < < process ;
2020-02-01 09:27:25 +00:00
# endif
2019-03-27 13:38:32 +00:00
ASSERT ( process . is_dead ( ) ) ;
g_processes - > remove ( & process ) ;
}
2018-11-09 00:25:31 +00:00
delete & process ;
2020-02-05 16:42:43 +00:00
return siginfo ;
2018-11-07 22:59:49 +00:00
}
2020-02-05 16:42:43 +00:00
KResultOr < siginfo_t > Process : : do_waitid ( idtype_t idtype , int id , int options )
2018-10-23 22:20:34 +00:00
{
2020-02-05 16:42:43 +00:00
if ( idtype = = P_PID ) {
2018-11-07 22:59:49 +00:00
InterruptDisabler disabler ;
2020-02-05 16:42:43 +00:00
if ( idtype = = P_PID & & ! Process : : from_pid ( id ) )
return KResult ( - ECHILD ) ;
2018-11-07 22:59:49 +00:00
}
2018-11-16 23:11:08 +00:00
if ( options & WNOHANG ) {
2019-07-14 09:35:49 +00:00
// FIXME: Figure out what WNOHANG should do with stopped children.
2020-02-05 16:42:43 +00:00
if ( idtype = = P_ALL ) {
2018-11-16 23:11:08 +00:00
InterruptDisabler disabler ;
2020-02-06 13:12:20 +00:00
siginfo_t siginfo ;
memset ( & siginfo , 0 , sizeof ( siginfo ) ) ;
2020-02-05 16:42:43 +00:00
for_each_child ( [ & siginfo ] ( Process & process ) {
if ( process . is_dead ( ) )
siginfo = reap ( process ) ;
2019-07-14 09:35:49 +00:00
return IterationDecision : : Continue ;
2018-11-16 23:11:08 +00:00
} ) ;
2020-02-05 16:42:43 +00:00
return siginfo ;
} else if ( idtype = = P_PID ) {
2019-02-26 23:02:01 +00:00
InterruptDisabler disabler ;
2020-02-05 16:42:43 +00:00
auto * waitee_process = Process : : from_pid ( id ) ;
2018-11-16 23:11:08 +00:00
if ( ! waitee_process )
2020-02-05 16:42:43 +00:00
return KResult ( - ECHILD ) ;
if ( waitee_process - > is_dead ( ) )
return reap ( * waitee_process ) ;
} else {
// FIXME: Implement other PID specs.
return KResult ( - EINVAL ) ;
2018-11-16 23:11:08 +00:00
}
}
2020-02-05 16:42:43 +00:00
pid_t waitee_pid ;
// FIXME: WaitBlocker should support idtype/id specs directly.
if ( idtype = = P_ALL ) {
waitee_pid = - 1 ;
} else if ( idtype = = P_PID ) {
waitee_pid = id ;
} else {
// FIXME: Implement other PID specs.
return KResult ( - EINVAL ) ;
}
2020-02-17 14:04:27 +00:00
if ( Thread : : current - > block < Thread : : WaitBlocker > ( options , waitee_pid ) ! = Thread : : BlockResult : : WokeNormally )
2020-02-05 16:42:43 +00:00
return KResult ( - EINTR ) ;
2019-07-14 09:35:49 +00:00
InterruptDisabler disabler ;
// NOTE: If waitee was -1, m_waitee_pid will have been filled in by the scheduler.
2019-07-18 16:05:19 +00:00
Process * waitee_process = Process : : from_pid ( waitee_pid ) ;
2019-09-08 11:54:48 +00:00
if ( ! waitee_process )
2020-02-05 16:42:43 +00:00
return KResult ( - ECHILD ) ;
2019-09-08 11:54:48 +00:00
2018-11-09 00:25:31 +00:00
ASSERT ( waitee_process ) ;
2019-07-14 09:35:49 +00:00
if ( waitee_process - > is_dead ( ) ) {
2020-02-05 16:42:43 +00:00
return reap ( * waitee_process ) ;
2019-07-14 09:35:49 +00:00
} else {
2020-01-27 20:20:58 +00:00
auto * waitee_thread = Thread : : from_tid ( waitee_pid ) ;
if ( ! waitee_thread )
2020-02-05 16:42:43 +00:00
return KResult ( - ECHILD ) ;
2020-01-27 19:47:10 +00:00
ASSERT ( waitee_thread - > state ( ) = = Thread : : State : : Stopped ) ;
2020-02-05 16:42:43 +00:00
siginfo_t siginfo ;
2020-02-06 13:12:20 +00:00
memset ( & siginfo , 0 , sizeof ( siginfo ) ) ;
2020-02-05 16:42:43 +00:00
siginfo . si_signo = SIGCHLD ;
siginfo . si_pid = waitee_process - > pid ( ) ;
siginfo . si_uid = waitee_process - > uid ( ) ;
2020-04-10 14:27:23 +00:00
siginfo . si_code = CLD_STOPPED ;
siginfo . si_status = waitee_thread - > m_stop_signal ;
2020-02-05 16:42:43 +00:00
return siginfo ;
2019-07-14 09:35:49 +00:00
}
2020-02-05 16:42:43 +00:00
}
pid_t Process : : sys $ waitid ( const Syscall : : SC_waitid_params * user_params )
{
REQUIRE_PROMISE ( stdio ) ;
2020-01-06 13:31:54 +00:00
2020-02-05 16:42:43 +00:00
Syscall : : SC_waitid_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
if ( ! validate_write_typed ( params . infop ) )
return - EFAULT ;
2020-02-05 18:14:56 +00:00
# ifdef PROCESS_DEBUG
2020-02-05 16:42:43 +00:00
dbg ( ) < < " sys$waitid( " < < params . idtype < < " , " < < params . id < < " , " < < params . infop < < " , " < < params . options < < " ) " ;
2020-02-05 18:14:56 +00:00
# endif
2020-02-05 16:42:43 +00:00
auto siginfo_or_error = do_waitid ( static_cast < idtype_t > ( params . idtype ) , params . id , params . options ) ;
if ( siginfo_or_error . is_error ( ) )
return siginfo_or_error . error ( ) ;
2020-03-08 12:14:35 +00:00
// While we waited, the process lock was dropped. This gave other threads
// the opportunity to mess with the memory. For example, it could free the
// region, and map it to a region to which it has no write permissions.
// Therefore, we need to re-validate the pointer.
if ( ! validate_write_typed ( params . infop ) )
return - EFAULT ;
2020-02-05 16:42:43 +00:00
copy_to_user ( params . infop , & siginfo_or_error . value ( ) ) ;
return 0 ;
2018-10-16 09:01:38 +00:00
}
2020-01-29 17:31:15 +00:00
bool Process : : validate_read_from_kernel ( VirtualAddress vaddr , size_t size ) const
2018-10-26 22:14:24 +00:00
{
2019-06-07 10:56:50 +00:00
if ( vaddr . is_null ( ) )
2019-04-15 21:48:31 +00:00
return false ;
2020-01-02 01:09:25 +00:00
return MM . validate_kernel_read ( * this , vaddr , size ) ;
2018-10-26 22:14:24 +00:00
}
2020-01-29 17:31:15 +00:00
bool Process : : validate_read ( const void * address , size_t size ) const
2018-11-16 14:41:48 +00:00
{
2019-01-25 00:39:15 +00:00
if ( ! size )
return false ;
2020-01-27 11:43:21 +00:00
return MM . validate_user_read ( * this , VirtualAddress ( address ) , size ) ;
2018-11-16 14:41:48 +00:00
}
2020-01-29 17:31:15 +00:00
bool Process : : validate_write ( void * address , size_t size ) const
2018-11-16 14:41:48 +00:00
{
2019-01-25 00:39:15 +00:00
if ( ! size )
return false ;
2020-01-27 11:43:21 +00:00
return MM . validate_user_write ( * this , VirtualAddress ( address ) , size ) ;
2018-11-16 14:41:48 +00:00
}
2018-11-02 11:56:51 +00:00
pid_t Process : : sys $ getsid ( pid_t pid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-02 11:56:51 +00:00
if ( pid = = 0 )
return m_sid ;
InterruptDisabler disabler ;
2018-11-07 20:38:18 +00:00
auto * process = Process : : from_pid ( pid ) ;
2018-11-02 11:56:51 +00:00
if ( ! process )
return - ESRCH ;
if ( m_sid ! = process - > m_sid )
return - EPERM ;
return process - > m_sid ;
}
pid_t Process : : sys $ setsid ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2018-11-02 11:56:51 +00:00
InterruptDisabler disabler ;
bool found_process_with_same_pgid_as_my_pid = false ;
2019-06-07 09:43:58 +00:00
Process : : for_each_in_pgrp ( pid ( ) , [ & ] ( auto & ) {
2018-11-07 20:38:18 +00:00
found_process_with_same_pgid_as_my_pid = true ;
2019-08-22 19:12:55 +00:00
return IterationDecision : : Break ;
2018-11-02 11:56:51 +00:00
} ) ;
if ( found_process_with_same_pgid_as_my_pid )
return - EPERM ;
m_sid = m_pid ;
m_pgid = m_pid ;
2020-01-25 13:53:48 +00:00
m_tty = nullptr ;
2018-11-02 11:56:51 +00:00
return m_sid ;
}
pid_t Process : : sys $ getpgid ( pid_t pid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-02 11:56:51 +00:00
if ( pid = = 0 )
return m_pgid ;
InterruptDisabler disabler ; // FIXME: Use a ProcessHandle
2018-11-07 20:38:18 +00:00
auto * process = Process : : from_pid ( pid ) ;
2018-11-02 11:56:51 +00:00
if ( ! process )
return - ESRCH ;
return process - > m_pgid ;
}
pid_t Process : : sys $ getpgrp ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-02 11:56:51 +00:00
return m_pgid ;
}
static pid_t get_sid_from_pgid ( pid_t pgid )
{
InterruptDisabler disabler ;
2018-11-07 20:38:18 +00:00
auto * group_leader = Process : : from_pid ( pgid ) ;
2018-11-02 11:56:51 +00:00
if ( ! group_leader )
return - 1 ;
return group_leader - > sid ( ) ;
}
int Process : : sys $ setpgid ( pid_t specified_pid , pid_t specified_pgid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2018-11-02 11:56:51 +00:00
InterruptDisabler disabler ; // FIXME: Use a ProcessHandle
pid_t pid = specified_pid ? specified_pid : m_pid ;
2020-01-02 18:40:04 +00:00
if ( specified_pgid < 0 ) {
// The value of the pgid argument is less than 0, or is not a value supported by the implementation.
2018-11-02 11:56:51 +00:00
return - EINVAL ;
2020-01-02 18:40:04 +00:00
}
2018-11-07 20:38:18 +00:00
auto * process = Process : : from_pid ( pid ) ;
2018-11-02 11:56:51 +00:00
if ( ! process )
return - ESRCH ;
2020-01-02 18:40:04 +00:00
if ( process ! = this & & process - > ppid ( ) ! = m_pid ) {
// The value of the pid argument does not match the process ID
// of the calling process or of a child process of the calling process.
return - ESRCH ;
}
if ( process - > pid ( ) = = process - > sid ( ) ) {
// The process indicated by the pid argument is a session leader.
return - EPERM ;
}
if ( process - > ppid ( ) = = m_pid & & process - > sid ( ) ! = sid ( ) ) {
// The value of the pid argument matches the process ID of a child
// process of the calling process and the child process is not in
// the same session as the calling process.
return - EPERM ;
}
2018-11-02 11:56:51 +00:00
pid_t new_pgid = specified_pgid ? specified_pgid : process - > m_pid ;
pid_t current_sid = get_sid_from_pgid ( process - > m_pgid ) ;
pid_t new_sid = get_sid_from_pgid ( new_pgid ) ;
if ( current_sid ! = new_sid ) {
// Can't move a process between sessions.
return - EPERM ;
}
// FIXME: There are more EPERM conditions to check for here..
process - > m_pgid = new_pgid ;
return 0 ;
}
2018-11-02 12:14:25 +00:00
2018-11-16 12:11:21 +00:00
int Process : : sys $ ioctl ( int fd , unsigned request , unsigned arg )
2018-11-02 12:14:25 +00:00
{
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-11-02 12:14:25 +00:00
return - EBADF ;
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2019-06-13 20:03:04 +00:00
return description - > file ( ) . ioctl ( * description , request , arg ) ;
2018-11-02 12:14:25 +00:00
}
2018-11-05 18:01:22 +00:00
int Process : : sys $ getdtablesize ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-05 18:01:22 +00:00
return m_max_open_file_descriptors ;
}
int Process : : sys $ dup ( int old_fd )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( old_fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-11-05 18:01:22 +00:00
return - EBADF ;
2020-01-20 12:18:54 +00:00
int new_fd = alloc_fd ( ) ;
2019-04-06 12:54:32 +00:00
if ( new_fd < 0 )
return new_fd ;
2019-06-13 20:03:04 +00:00
m_fds [ new_fd ] . set ( * description ) ;
2018-11-05 18:01:22 +00:00
return new_fd ;
}
int Process : : sys $ dup2 ( int old_fd , int new_fd )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( old_fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2018-11-05 18:01:22 +00:00
return - EBADF ;
2019-04-06 12:54:32 +00:00
if ( new_fd < 0 | | new_fd > = m_max_open_file_descriptors )
return - EINVAL ;
2019-06-13 20:03:04 +00:00
m_fds [ new_fd ] . set ( * description ) ;
2018-11-05 18:01:22 +00:00
return new_fd ;
}
2018-11-06 09:46:40 +00:00
2019-01-23 05:53:01 +00:00
int Process : : sys $ sigprocmask ( int how , const sigset_t * set , sigset_t * old_set )
2018-11-10 22:29:07 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-11 14:36:40 +00:00
if ( old_set ) {
2019-02-21 20:33:52 +00:00
if ( ! validate_write_typed ( old_set ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-02-17 14:04:27 +00:00
copy_to_user ( old_set , & Thread : : current - > m_signal_mask ) ;
2018-11-11 14:36:40 +00:00
}
if ( set ) {
2018-11-16 15:23:39 +00:00
if ( ! validate_read_typed ( set ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-01-05 17:00:15 +00:00
sigset_t set_value ;
2020-01-13 07:00:35 +00:00
copy_from_user ( & set_value , set ) ;
2018-11-11 14:36:40 +00:00
switch ( how ) {
case SIG_BLOCK :
2020-02-17 14:04:27 +00:00
Thread : : current - > m_signal_mask & = ~ set_value ;
2018-11-11 14:36:40 +00:00
break ;
case SIG_UNBLOCK :
2020-02-17 14:04:27 +00:00
Thread : : current - > m_signal_mask | = set_value ;
2018-11-11 14:36:40 +00:00
break ;
case SIG_SETMASK :
2020-02-17 14:04:27 +00:00
Thread : : current - > m_signal_mask = set_value ;
2018-11-11 14:36:40 +00:00
break ;
default :
return - EINVAL ;
}
2018-11-10 22:29:07 +00:00
}
return 0 ;
}
2019-01-23 05:53:01 +00:00
int Process : : sys $ sigpending ( sigset_t * set )
2018-11-10 22:29:07 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-02-21 20:33:52 +00:00
if ( ! validate_write_typed ( set ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-02-17 14:04:27 +00:00
copy_to_user ( set , & Thread : : current - > m_pending_signals ) ;
2018-11-10 22:29:07 +00:00
return 0 ;
}
2019-01-23 05:53:01 +00:00
int Process : : sys $ sigaction ( int signum , const sigaction * act , sigaction * old_act )
2018-11-06 09:46:40 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-12-24 21:22:19 +00:00
if ( signum < 1 | | signum > = 32 | | signum = = SIGKILL | | signum = = SIGSTOP )
2018-11-06 09:46:40 +00:00
return - EINVAL ;
2018-11-16 15:23:39 +00:00
if ( ! validate_read_typed ( act ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2019-02-07 10:24:09 +00:00
InterruptDisabler disabler ; // FIXME: This should use a narrower lock. Maybe a way to ignore signals temporarily?
2020-02-17 14:04:27 +00:00
auto & action = Thread : : current - > m_signal_action_data [ signum ] ;
2018-11-07 09:48:44 +00:00
if ( old_act ) {
2018-11-16 15:23:39 +00:00
if ( ! validate_write_typed ( old_act ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-01-20 12:38:31 +00:00
copy_to_user ( & old_act - > sa_flags , & action . flags ) ;
2020-01-05 17:00:15 +00:00
copy_to_user ( & old_act - > sa_sigaction , & action . handler_or_sigaction , sizeof ( action . handler_or_sigaction ) ) ;
2018-11-07 09:48:44 +00:00
}
2020-01-13 07:00:35 +00:00
copy_from_user ( & action . flags , & act - > sa_flags ) ;
2020-01-20 12:38:31 +00:00
copy_from_user ( & action . handler_or_sigaction , & act - > sa_sigaction , sizeof ( action . handler_or_sigaction ) ) ;
2018-11-06 09:46:40 +00:00
return 0 ;
}
2018-11-07 00:38:51 +00:00
2020-02-15 23:30:00 +00:00
int Process : : sys $ getgroups ( ssize_t count , gid_t * user_gids )
2018-11-07 00:38:51 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-11-07 00:38:51 +00:00
if ( count < 0 )
return - EINVAL ;
if ( ! count )
2020-01-02 22:45:52 +00:00
return m_extra_gids . size ( ) ;
if ( count ! = ( int ) m_extra_gids . size ( ) )
2018-11-07 00:38:51 +00:00
return - EINVAL ;
2020-02-15 23:30:00 +00:00
if ( ! validate_write_typed ( user_gids , m_extra_gids . size ( ) ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-02-15 23:30:00 +00:00
Vector < gid_t > gids ;
2020-01-02 22:45:52 +00:00
for ( auto gid : m_extra_gids )
2020-02-15 23:30:00 +00:00
gids . append ( gid ) ;
copy_to_user ( user_gids , gids . data ( ) , sizeof ( gid_t ) * count ) ;
2018-11-07 00:38:51 +00:00
return 0 ;
}
2020-02-15 23:27:10 +00:00
int Process : : sys $ setgroups ( ssize_t count , const gid_t * user_gids )
2018-11-07 00:38:51 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( id ) ;
2019-02-25 21:06:55 +00:00
if ( count < 0 )
return - EINVAL ;
2019-02-21 22:35:07 +00:00
if ( ! is_superuser ( ) )
2018-11-07 00:38:51 +00:00
return - EPERM ;
2020-02-15 23:27:10 +00:00
if ( count & & ! validate_read ( user_gids , count ) )
2018-11-16 15:10:59 +00:00
return - EFAULT ;
2020-02-15 23:27:10 +00:00
Vector < gid_t > gids ;
gids . resize ( count ) ;
copy_from_user ( gids . data ( ) , user_gids , sizeof ( gid_t ) * count ) ;
2020-02-18 09:19:32 +00:00
HashTable < gid_t > unique_extra_gids ;
for ( auto & gid : gids ) {
if ( gid ! = m_gid )
unique_extra_gids . set ( gid ) ;
}
m_extra_gids . resize ( unique_extra_gids . size ( ) ) ;
size_t i = 0 ;
for ( auto & gid : unique_extra_gids ) {
if ( gid = = m_gid )
2020-01-02 22:45:52 +00:00
continue ;
2020-02-18 09:19:32 +00:00
m_extra_gids [ i + + ] = gid ;
2020-01-02 22:45:52 +00:00
}
2018-11-07 00:38:51 +00:00
return 0 ;
}
2018-11-18 13:57:41 +00:00
2020-01-06 10:05:59 +00:00
int Process : : sys $ mkdir ( const char * user_path , size_t path_length , mode_t mode )
2018-11-18 13:57:41 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( cpath ) ;
2020-01-06 10:05:59 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . mkdir ( path . value ( ) , mode & ~ umask ( ) , current_directory ( ) ) ;
2018-11-18 13:57:41 +00:00
}
2018-12-03 00:12:26 +00:00
2020-01-06 10:32:25 +00:00
int Process : : sys $ realpath ( const Syscall : : SC_realpath_params * user_params )
2019-08-25 16:17:05 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2019-08-25 16:17:05 +00:00
2020-01-06 10:32:25 +00:00
Syscall : : SC_realpath_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-06 10:32:25 +00:00
2020-01-11 11:47:47 +00:00
if ( ! validate_write ( params . buffer . data , params . buffer . size ) )
2019-08-25 16:17:05 +00:00
return - EFAULT ;
2020-01-11 11:47:47 +00:00
auto path = get_syscall_path_argument ( params . path ) ;
2020-01-06 10:32:25 +00:00
if ( path . is_error ( ) )
return path . error ( ) ;
auto custody_or_error = VFS : : the ( ) . resolve_path ( path . value ( ) , current_directory ( ) ) ;
2019-08-25 16:17:05 +00:00
if ( custody_or_error . is_error ( ) )
return custody_or_error . error ( ) ;
auto & custody = custody_or_error . value ( ) ;
2020-01-15 11:05:49 +00:00
auto absolute_path = custody - > absolute_path ( ) ;
2019-08-25 16:17:05 +00:00
2020-01-15 11:05:49 +00:00
if ( absolute_path . length ( ) + 1 > params . buffer . size )
2020-01-06 10:32:25 +00:00
return - ENAMETOOLONG ;
2020-01-15 11:05:49 +00:00
copy_to_user ( params . buffer . data , absolute_path . characters ( ) , absolute_path . length ( ) + 1 ) ;
2019-08-25 16:17:05 +00:00
return 0 ;
} ;
2019-01-23 05:53:01 +00:00
clock_t Process : : sys $ times ( tms * times )
2018-12-03 00:12:26 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2018-12-03 00:12:26 +00:00
if ( ! validate_write_typed ( times ) )
return - EFAULT ;
2020-01-20 12:38:31 +00:00
copy_to_user ( & times - > tms_utime , & m_ticks_in_user ) ;
copy_to_user ( & times - > tms_stime , & m_ticks_in_kernel ) ;
copy_to_user ( & times - > tms_cutime , & m_ticks_in_user_for_dead_children ) ;
copy_to_user ( & times - > tms_cstime , & m_ticks_in_kernel_for_dead_children ) ;
2019-05-17 18:19:29 +00:00
return g_uptime & 0x7fffffff ;
2018-12-03 00:12:26 +00:00
}
2019-01-09 01:29:11 +00:00
2019-01-15 22:12:20 +00:00
int Process : : sys $ select ( const Syscall : : SC_select_params * params )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-06-06 15:46:41 +00:00
// FIXME: Return -EINVAL if timeout is invalid.
2019-01-15 22:12:20 +00:00
if ( ! validate_read_typed ( params ) )
return - EFAULT ;
2019-11-10 14:32:59 +00:00
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2020-01-05 08:13:00 +00:00
int nfds = params - > nfds ;
fd_set * readfds = params - > readfds ;
fd_set * writefds = params - > writefds ;
fd_set * exceptfds = params - > exceptfds ;
timeval * timeout = params - > timeout ;
2019-11-10 14:32:59 +00:00
if ( writefds & & ! validate_write_typed ( writefds ) )
2019-01-15 22:12:20 +00:00
return - EFAULT ;
2019-11-10 14:32:59 +00:00
if ( readfds & & ! validate_write_typed ( readfds ) )
2019-01-15 22:12:20 +00:00
return - EFAULT ;
2019-11-10 14:32:59 +00:00
if ( exceptfds & & ! validate_write_typed ( exceptfds ) )
2019-01-15 22:12:20 +00:00
return - EFAULT ;
2019-11-10 14:32:59 +00:00
if ( timeout & & ! validate_read_typed ( timeout ) )
2019-01-15 22:12:20 +00:00
return - EFAULT ;
2019-11-10 14:32:59 +00:00
if ( nfds < 0 )
2019-06-06 15:46:41 +00:00
return - EINVAL ;
2019-01-15 22:12:20 +00:00
2019-11-10 14:32:59 +00:00
timeval computed_timeout ;
2019-07-18 15:39:49 +00:00
bool select_has_timeout = false ;
2019-11-10 14:32:59 +00:00
if ( timeout & & ( timeout - > tv_sec | | timeout - > tv_usec ) ) {
2020-03-13 23:02:06 +00:00
timeval_add ( Scheduler : : time_since_boot ( ) , * timeout , computed_timeout ) ;
2019-07-18 15:39:49 +00:00
select_has_timeout = true ;
2019-02-01 02:50:06 +00:00
}
2019-01-15 22:12:20 +00:00
2019-07-19 07:04:12 +00:00
Thread : : SelectBlocker : : FDVector rfds ;
Thread : : SelectBlocker : : FDVector wfds ;
Thread : : SelectBlocker : : FDVector efds ;
2019-07-18 15:39:49 +00:00
2019-06-07 09:43:58 +00:00
auto transfer_fds = [ & ] ( auto * fds , auto & vector ) - > int {
2019-05-18 00:00:54 +00:00
vector . clear_with_capacity ( ) ;
2019-06-06 15:46:41 +00:00
if ( ! fds )
2019-01-30 18:01:31 +00:00
return 0 ;
2020-01-05 08:13:00 +00:00
for ( int fd = 0 ; fd < nfds ; + + fd ) {
2019-06-06 15:46:41 +00:00
if ( FD_ISSET ( fd , fds ) ) {
2019-12-02 08:33:37 +00:00
if ( ! file_description ( fd ) ) {
2020-01-21 15:14:39 +00:00
dbg ( ) < < " sys$select: Bad fd number " < < fd ;
2019-01-30 18:01:31 +00:00
return - EBADF ;
2019-12-02 08:33:37 +00:00
}
2019-06-06 15:46:41 +00:00
vector . append ( fd ) ;
2019-01-30 18:01:31 +00:00
}
2019-01-15 22:12:20 +00:00
}
2019-01-30 18:01:31 +00:00
return 0 ;
2019-01-15 22:12:20 +00:00
} ;
2019-11-10 14:32:59 +00:00
if ( int error = transfer_fds ( writefds , wfds ) )
2019-01-30 18:01:31 +00:00
return error ;
2019-11-10 14:32:59 +00:00
if ( int error = transfer_fds ( readfds , rfds ) )
2019-01-30 18:01:31 +00:00
return error ;
2019-11-10 14:32:59 +00:00
if ( int error = transfer_fds ( exceptfds , efds ) )
2019-02-26 23:02:01 +00:00
return error ;
2019-01-15 22:12:20 +00:00
2019-05-19 08:24:28 +00:00
# if defined(DEBUG_IO) || defined(DEBUG_POLL_SELECT)
2020-02-29 11:51:44 +00:00
dbg ( ) < < " selecting on (read: " < < rfds . size ( ) < < " , write: " < < wfds . size ( ) < < " ), timeout= " < < timeout ;
2019-01-16 16:20:58 +00:00
# endif
2019-11-10 14:32:59 +00:00
if ( ! timeout | | select_has_timeout ) {
2020-02-17 14:04:27 +00:00
if ( Thread : : current - > block < Thread : : SelectBlocker > ( computed_timeout , select_has_timeout , rfds , wfds , efds ) ! = Thread : : BlockResult : : WokeNormally )
2019-07-21 11:31:20 +00:00
return - EINTR ;
2020-03-07 22:38:47 +00:00
// While we blocked, the process lock was dropped. This gave other threads
// the opportunity to mess with the memory. For example, it could free the
// region, and map it to a region to which it has no write permissions.
// Therefore, we need to re-validate all pointers.
if ( writefds & & ! validate_write_typed ( writefds ) )
return - EFAULT ;
if ( readfds & & ! validate_write_typed ( readfds ) )
return - EFAULT ;
// See the fixme below.
if ( exceptfds & & ! validate_write_typed ( exceptfds ) )
return - EFAULT ;
2019-07-21 11:31:20 +00:00
}
2019-01-15 22:12:20 +00:00
2019-06-06 15:46:41 +00:00
int marked_fd_count = 0 ;
2019-06-07 09:43:58 +00:00
auto mark_fds = [ & ] ( auto * fds , auto & vector , auto should_mark ) {
2019-06-06 15:46:41 +00:00
if ( ! fds )
return ;
FD_ZERO ( fds ) ;
for ( int fd : vector ) {
2020-01-07 14:53:42 +00:00
if ( auto description = file_description ( fd ) ; description & & should_mark ( * description ) ) {
2019-06-06 15:46:41 +00:00
FD_SET ( fd , fds ) ;
+ + marked_fd_count ;
2019-01-15 22:12:20 +00:00
}
}
2019-06-06 15:46:41 +00:00
} ;
2019-11-10 14:32:59 +00:00
mark_fds ( readfds , rfds , [ ] ( auto & description ) { return description . can_read ( ) ; } ) ;
mark_fds ( writefds , wfds , [ ] ( auto & description ) { return description . can_write ( ) ; } ) ;
// FIXME: We should also mark exceptfds as appropriate.
2019-07-20 09:05:52 +00:00
2019-06-06 15:46:41 +00:00
return marked_fd_count ;
2019-01-15 22:12:20 +00:00
}
2019-01-16 16:20:58 +00:00
2019-01-23 06:27:41 +00:00
int Process : : sys $ poll ( pollfd * fds , int nfds , int timeout )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-01-23 06:27:41 +00:00
if ( ! validate_read_typed ( fds ) )
return - EFAULT ;
2019-01-23 07:03:31 +00:00
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2019-07-19 07:04:12 +00:00
Thread : : SelectBlocker : : FDVector rfds ;
Thread : : SelectBlocker : : FDVector wfds ;
2019-07-18 15:39:49 +00:00
2019-01-23 07:03:31 +00:00
for ( int i = 0 ; i < nfds ; + + i ) {
if ( fds [ i ] . events & POLLIN )
2019-07-18 15:39:49 +00:00
rfds . append ( fds [ i ] . fd ) ;
2019-01-23 07:03:31 +00:00
if ( fds [ i ] . events & POLLOUT )
2019-07-18 15:39:49 +00:00
wfds . append ( fds [ i ] . fd ) ;
2019-01-23 07:03:31 +00:00
}
2019-07-18 15:39:49 +00:00
timeval actual_timeout ;
bool has_timeout = false ;
2019-05-18 01:59:48 +00:00
if ( timeout > = 0 ) {
// poll is in ms, we want s/us.
struct timeval tvtimeout ;
tvtimeout . tv_sec = 0 ;
while ( timeout > = 1000 ) {
tvtimeout . tv_sec + = 1 ;
timeout - = 1000 ;
}
tvtimeout . tv_usec = timeout * 1000 ;
2020-03-13 23:02:06 +00:00
timeval_add ( Scheduler : : time_since_boot ( ) , tvtimeout , actual_timeout ) ;
2019-07-18 15:39:49 +00:00
has_timeout = true ;
2019-05-18 01:59:48 +00:00
}
2019-05-19 08:24:28 +00:00
# if defined(DEBUG_IO) || defined(DEBUG_POLL_SELECT)
2020-02-29 11:51:44 +00:00
dbg ( ) < < " polling on (read: " < < rfds . size ( ) < < " , write: " < < wfds . size ( ) < < " ), timeout= " < < timeout ;
2019-05-19 08:24:28 +00:00
# endif
2019-08-02 13:18:47 +00:00
if ( has_timeout | | timeout < 0 ) {
2020-02-17 14:04:27 +00:00
if ( Thread : : current - > block < Thread : : SelectBlocker > ( actual_timeout , has_timeout , rfds , wfds , Thread : : SelectBlocker : : FDVector ( ) ) ! = Thread : : BlockResult : : WokeNormally )
2019-07-21 11:31:20 +00:00
return - EINTR ;
}
2019-01-23 07:03:31 +00:00
int fds_with_revents = 0 ;
for ( int i = 0 ; i < nfds ; + + i ) {
2020-01-07 14:53:42 +00:00
auto description = file_description ( fds [ i ] . fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description ) {
2019-01-23 07:03:31 +00:00
fds [ i ] . revents = POLLNVAL ;
continue ;
}
fds [ i ] . revents = 0 ;
2019-06-13 20:03:04 +00:00
if ( fds [ i ] . events & POLLIN & & description - > can_read ( ) )
2019-01-23 07:03:31 +00:00
fds [ i ] . revents | = POLLIN ;
2019-06-13 20:03:04 +00:00
if ( fds [ i ] . events & POLLOUT & & description - > can_write ( ) )
2019-01-23 07:03:31 +00:00
fds [ i ] . revents | = POLLOUT ;
if ( fds [ i ] . revents )
+ + fds_with_revents ;
}
return fds_with_revents ;
2019-01-23 06:27:41 +00:00
}
2019-05-30 18:23:50 +00:00
Custody & Process : : current_directory ( )
2019-01-16 16:20:58 +00:00
{
2019-05-30 18:23:50 +00:00
if ( ! m_cwd )
m_cwd = VFS : : the ( ) . root_custody ( ) ;
return * m_cwd ;
2019-01-16 16:20:58 +00:00
}
2019-01-22 06:03:44 +00:00
2020-01-10 20:26:47 +00:00
int Process : : sys $ link ( const Syscall : : SC_link_params * user_params )
2019-02-21 12:26:40 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( cpath ) ;
2020-01-10 20:26:47 +00:00
Syscall : : SC_link_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-10 20:26:47 +00:00
auto old_path = validate_and_copy_string_from_user ( params . old_path ) ;
auto new_path = validate_and_copy_string_from_user ( params . new_path ) ;
if ( old_path . is_null ( ) | | new_path . is_null ( ) )
2019-02-21 12:26:40 +00:00
return - EFAULT ;
2020-01-10 20:26:47 +00:00
return VFS : : the ( ) . link ( old_path , new_path , current_directory ( ) ) ;
2019-02-21 12:26:40 +00:00
}
2020-01-09 11:41:15 +00:00
int Process : : sys $ unlink ( const char * user_path , size_t path_length )
2019-01-22 06:03:44 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( cpath ) ;
2020-01-09 11:41:15 +00:00
if ( ! validate_read ( user_path , path_length ) )
return - EFAULT ;
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . unlink ( path . value ( ) , current_directory ( ) ) ;
2019-01-22 06:03:44 +00:00
}
2019-01-25 01:09:29 +00:00
2020-01-11 09:31:33 +00:00
int Process : : sys $ symlink ( const Syscall : : SC_symlink_params * user_params )
2019-03-02 00:50:34 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( cpath ) ;
2020-01-11 09:31:33 +00:00
Syscall : : SC_symlink_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-11 11:47:47 +00:00
auto target = get_syscall_path_argument ( params . target ) ;
2020-01-11 09:31:33 +00:00
if ( target . is_error ( ) )
return target . error ( ) ;
2020-01-11 11:47:47 +00:00
auto linkpath = get_syscall_path_argument ( params . linkpath ) ;
2020-01-11 09:31:33 +00:00
if ( linkpath . is_error ( ) )
return linkpath . error ( ) ;
return VFS : : the ( ) . symlink ( target . value ( ) , linkpath . value ( ) , current_directory ( ) ) ;
2019-03-02 00:50:34 +00:00
}
2020-01-11 11:47:47 +00:00
KResultOr < String > Process : : get_syscall_path_argument ( const char * user_path , size_t path_length ) const
2019-01-28 03:16:01 +00:00
{
2020-01-06 10:05:59 +00:00
if ( path_length = = 0 )
return KResult ( - EINVAL ) ;
if ( path_length > PATH_MAX )
return KResult ( - ENAMETOOLONG ) ;
if ( ! validate_read ( user_path , path_length ) )
return KResult ( - EFAULT ) ;
return copy_string_from_user ( user_path , path_length ) ;
2019-01-28 03:16:01 +00:00
}
2020-01-11 11:47:47 +00:00
KResultOr < String > Process : : get_syscall_path_argument ( const Syscall : : StringArgument & path ) const
{
return get_syscall_path_argument ( path . characters , path . length ) ;
}
2020-01-06 10:05:59 +00:00
int Process : : sys $ rmdir ( const char * user_path , size_t path_length )
2019-01-29 03:55:08 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( cpath ) ;
2020-01-06 10:05:59 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . rmdir ( path . value ( ) , current_directory ( ) ) ;
}
int Process : : sys $ chmod ( const char * user_path , size_t path_length , mode_t mode )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( fattr ) ;
2020-01-06 10:05:59 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . chmod ( path . value ( ) , mode , current_directory ( ) ) ;
2019-01-29 03:55:08 +00:00
}
2019-01-30 17:26:19 +00:00
2019-03-01 09:39:19 +00:00
int Process : : sys $ fchmod ( int fd , mode_t mode )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( fattr ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-03-01 09:39:19 +00:00
return - EBADF ;
2020-01-03 19:14:56 +00:00
return description - > chmod ( mode ) ;
2019-03-01 09:39:19 +00:00
}
2019-06-01 18:31:36 +00:00
int Process : : sys $ fchown ( int fd , uid_t uid , gid_t gid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( chown ) ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-06-01 18:31:36 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
return description - > chown ( uid , gid ) ;
2019-06-01 18:31:36 +00:00
}
2020-01-11 09:17:08 +00:00
int Process : : sys $ chown ( const Syscall : : SC_chown_params * user_params )
2019-02-27 11:32:53 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( chown ) ;
2020-01-11 09:17:08 +00:00
Syscall : : SC_chown_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-11 11:47:47 +00:00
auto path = get_syscall_path_argument ( params . path ) ;
2020-01-11 09:17:08 +00:00
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . chown ( path . value ( ) , params . uid , params . gid , current_directory ( ) ) ;
2019-02-27 11:32:53 +00:00
}
2019-02-06 17:45:21 +00:00
void Process : : finalize ( )
2019-01-30 17:26:19 +00:00
{
2020-02-17 14:04:27 +00:00
ASSERT ( Thread : : current = = g_finalizer ) ;
2020-02-01 09:27:25 +00:00
# ifdef PROCESS_DEBUG
2020-01-21 15:14:39 +00:00
dbg ( ) < < " Finalizing process " < < * this ;
2020-02-01 09:27:25 +00:00
# endif
2019-02-06 16:28:14 +00:00
2020-02-02 19:26:27 +00:00
if ( m_perf_event_buffer ) {
2020-03-01 19:58:48 +00:00
auto description_or_error = VFS : : the ( ) . open ( String : : format ( " perfcore.%d " , m_pid ) , O_CREAT | O_EXCL , 0400 , current_directory ( ) , UidAndGid { m_uid , m_gid } ) ;
2020-02-02 19:26:27 +00:00
if ( ! description_or_error . is_error ( ) ) {
auto & description = description_or_error . value ( ) ;
auto json = m_perf_event_buffer - > to_json ( m_pid , m_executable ? m_executable - > absolute_path ( ) : " " ) ;
description - > write ( json . data ( ) , json . size ( ) ) ;
}
}
2019-01-30 17:26:19 +00:00
m_fds . clear ( ) ;
2019-02-04 09:21:15 +00:00
m_tty = nullptr ;
2019-05-31 05:19:54 +00:00
m_executable = nullptr ;
m_cwd = nullptr ;
2020-01-10 22:14:04 +00:00
m_root_directory = nullptr ;
2020-01-12 18:42:01 +00:00
m_root_directory_relative_to_global_root = nullptr ;
2019-05-31 06:03:58 +00:00
2019-02-16 11:13:43 +00:00
disown_all_shared_buffers ( ) ;
2019-02-06 17:45:21 +00:00
{
InterruptDisabler disabler ;
2020-01-06 13:34:28 +00:00
if ( auto * parent_thread = Thread : : from_tid ( m_ppid ) ) {
if ( parent_thread - > m_signal_action_data [ SIGCHLD ] . flags & SA_NOCLDWAIT ) {
2019-03-01 14:47:07 +00:00
// NOTE: If the parent doesn't care about this process, let it go.
m_ppid = 0 ;
} else {
2020-01-06 13:34:28 +00:00
parent_thread - > send_signal ( SIGCHLD , this ) ;
2019-03-01 14:47:07 +00:00
}
2019-02-06 17:45:21 +00:00
}
2019-02-04 12:30:03 +00:00
}
2019-03-24 00:20:35 +00:00
2020-02-17 13:33:06 +00:00
m_regions . clear ( ) ;
2019-03-24 00:20:35 +00:00
m_dead = true ;
2019-01-30 17:26:19 +00:00
}
2019-02-03 17:53:18 +00:00
2019-02-06 17:45:21 +00:00
void Process : : die ( )
{
2019-11-10 07:49:23 +00:00
// Let go of the TTY, otherwise a slave PTY may keep the master PTY from
// getting an EOF when the last process using the slave PTY dies.
// If the master PTY owner relies on an EOF to know when to wait() on a
// slave owner, we have to allow the PTY pair to be torn down.
m_tty = nullptr ;
Kernel: Allow process with multiple threads to call exec and exit
This allows a process wich has more than 1 thread to call exec, even
from a thread. This kills all the other threads, but it won't wait for
them to finish, just makes sure that they are not in a running/runable
state.
In the case where a thread does exec, the new program PID will be the
thread TID, to keep the PID == TID in the new process.
This introduces a new function inside the Process class,
kill_threads_except_self which is called on exit() too (exit with
multiple threads wasn't properly working either).
Inside the Lock class, there is the need for a new function,
clear_waiters, which removes all the waiters from the
Process::big_lock. This is needed since after a exit/exec, there should
be no other threads waiting for this lock, the threads should be simply
killed. Only queued threads should wait for this lock at this point,
since blocked threads are handled in set_should_die.
2020-02-18 12:28:28 +00:00
kill_all_threads ( ) ;
2019-02-06 17:45:21 +00:00
}
2019-12-29 11:28:32 +00:00
size_t Process : : amount_dirty_private ( ) const
{
2019-12-29 11:45:58 +00:00
// FIXME: This gets a bit more complicated for Regions sharing the same underlying VMObject.
// The main issue I'm thinking of is when the VMObject has physical pages that none of the Regions are mapping.
// That's probably a situation that needs to be looked at in general.
2019-12-29 11:28:32 +00:00
size_t amount = 0 ;
for ( auto & region : m_regions ) {
if ( ! region . is_shared ( ) )
amount + = region . amount_dirty ( ) ;
}
return amount ;
}
2019-12-29 11:45:58 +00:00
size_t Process : : amount_clean_inode ( ) const
{
2020-03-01 10:23:23 +00:00
HashTable < const InodeVMObject * > vmobjects ;
2019-12-29 11:45:58 +00:00
for ( auto & region : m_regions ) {
if ( region . vmobject ( ) . is_inode ( ) )
2020-03-01 10:23:23 +00:00
vmobjects . set ( & static_cast < const InodeVMObject & > ( region . vmobject ( ) ) ) ;
2019-12-29 11:45:58 +00:00
}
size_t amount = 0 ;
for ( auto & vmobject : vmobjects )
amount + = vmobject - > amount_clean ( ) ;
return amount ;
}
2019-02-03 17:53:18 +00:00
size_t Process : : amount_virtual ( ) const
{
size_t amount = 0 ;
for ( auto & region : m_regions ) {
2019-06-27 11:34:28 +00:00
amount + = region . size ( ) ;
2019-02-03 17:53:18 +00:00
}
return amount ;
}
size_t Process : : amount_resident ( ) const
{
// FIXME: This will double count if multiple regions use the same physical page.
size_t amount = 0 ;
for ( auto & region : m_regions ) {
2019-06-27 11:34:28 +00:00
amount + = region . amount_resident ( ) ;
2019-02-03 17:53:18 +00:00
}
return amount ;
}
size_t Process : : amount_shared ( ) const
{
// FIXME: This will double count if multiple regions use the same physical page.
2019-06-21 16:40:24 +00:00
// FIXME: It doesn't work at the moment, since it relies on PhysicalPage ref counts,
2019-06-21 13:29:31 +00:00
// and each PhysicalPage is only reffed by its VMObject. This needs to be refactored
2019-06-21 16:40:24 +00:00
// so that every Region contributes +1 ref to each of its PhysicalPages.
2019-02-03 17:53:18 +00:00
size_t amount = 0 ;
for ( auto & region : m_regions ) {
2019-06-27 11:34:28 +00:00
amount + = region . amount_shared ( ) ;
2019-02-03 17:53:18 +00:00
}
return amount ;
}
2019-02-06 17:45:21 +00:00
2019-12-09 18:12:38 +00:00
size_t Process : : amount_purgeable_volatile ( ) const
{
size_t amount = 0 ;
for ( auto & region : m_regions ) {
if ( region . vmobject ( ) . is_purgeable ( ) & & static_cast < const PurgeableVMObject & > ( region . vmobject ( ) ) . is_volatile ( ) )
amount + = region . amount_resident ( ) ;
}
return amount ;
}
size_t Process : : amount_purgeable_nonvolatile ( ) const
{
size_t amount = 0 ;
for ( auto & region : m_regions ) {
if ( region . vmobject ( ) . is_purgeable ( ) & & ! static_cast < const PurgeableVMObject & > ( region . vmobject ( ) ) . is_volatile ( ) )
amount + = region . amount_resident ( ) ;
}
return amount ;
}
2020-01-11 19:48:43 +00:00
# define REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(domain) \
do { \
if ( domain = = AF_INET ) \
REQUIRE_PROMISE ( inet ) ; \
else if ( domain = = AF_LOCAL ) \
REQUIRE_PROMISE ( unix ) ; \
} while ( 0 )
2019-02-14 13:17:38 +00:00
int Process : : sys $ socket ( int domain , int type , int protocol )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( domain ) ;
2019-12-31 00:42:34 +00:00
if ( ( type & SOCK_TYPE_MASK ) = = SOCK_RAW & & ! is_superuser ( ) )
return - EACCES ;
2019-04-06 12:54:32 +00:00
int fd = alloc_fd ( ) ;
if ( fd < 0 )
return fd ;
2019-03-06 21:14:31 +00:00
auto result = Socket : : create ( domain , type , protocol ) ;
if ( result . is_error ( ) )
return result . error ( ) ;
2019-06-13 20:03:04 +00:00
auto description = FileDescription : : create ( * result . value ( ) ) ;
2020-01-03 02:29:59 +00:00
description - > set_readable ( true ) ;
description - > set_writable ( true ) ;
2019-02-14 14:17:30 +00:00
unsigned flags = 0 ;
if ( type & SOCK_CLOEXEC )
2019-02-17 09:41:37 +00:00
flags | = FD_CLOEXEC ;
2019-02-14 14:17:30 +00:00
if ( type & SOCK_NONBLOCK )
2019-06-13 20:03:04 +00:00
description - > set_blocking ( false ) ;
m_fds [ fd ] . set ( move ( description ) , flags ) ;
2019-02-14 13:17:38 +00:00
return fd ;
}
2019-02-14 13:38:30 +00:00
int Process : : sys $ bind ( int sockfd , const sockaddr * address , socklen_t address_length )
2019-02-14 13:17:38 +00:00
{
2019-02-14 13:38:30 +00:00
if ( ! validate_read ( address , address_length ) )
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-02-14 13:38:30 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-02-14 13:38:30 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
2019-03-06 21:14:31 +00:00
return socket . bind ( address , address_length ) ;
2019-02-14 13:17:38 +00:00
}
int Process : : sys $ listen ( int sockfd , int backlog )
{
2020-02-25 13:49:47 +00:00
if ( backlog < 0 )
return - EINVAL ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-02-14 14:17:30 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-02-14 14:17:30 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
2019-11-27 15:01:22 +00:00
if ( socket . is_connected ( ) )
return - EINVAL ;
2019-08-11 13:38:20 +00:00
return socket . listen ( backlog ) ;
2019-02-14 13:17:38 +00:00
}
2020-02-16 07:20:54 +00:00
int Process : : sys $ accept ( int accepting_socket_fd , sockaddr * user_address , socklen_t * user_address_size )
2019-02-14 13:17:38 +00:00
{
2020-01-17 10:12:06 +00:00
REQUIRE_PROMISE ( accept ) ;
2020-02-16 07:20:54 +00:00
if ( ! validate_write_typed ( user_address_size ) )
2019-02-14 14:17:30 +00:00
return - EFAULT ;
2020-02-16 07:20:54 +00:00
socklen_t address_size = 0 ;
copy_from_user ( & address_size , user_address_size ) ;
if ( ! validate_write ( user_address , address_size ) )
2019-02-14 14:17:30 +00:00
return - EFAULT ;
2019-04-06 12:54:32 +00:00
int accepted_socket_fd = alloc_fd ( ) ;
if ( accepted_socket_fd < 0 )
return accepted_socket_fd ;
2020-01-07 14:53:42 +00:00
auto accepting_socket_description = file_description ( accepting_socket_fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! accepting_socket_description )
2019-02-14 14:17:30 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! accepting_socket_description - > is_socket ( ) )
2019-02-14 14:17:30 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * accepting_socket_description - > socket ( ) ;
2019-02-14 14:17:30 +00:00
if ( ! socket . can_accept ( ) ) {
2019-07-18 08:39:00 +00:00
if ( accepting_socket_description - > is_blocking ( ) ) {
2020-02-17 14:04:27 +00:00
if ( Thread : : current - > block < Thread : : AcceptBlocker > ( * accepting_socket_description ) ! = Thread : : BlockResult : : WokeNormally )
2019-07-18 08:39:00 +00:00
return - EINTR ;
} else {
return - EAGAIN ;
}
2019-02-14 14:17:30 +00:00
}
2019-02-15 10:43:43 +00:00
auto accepted_socket = socket . accept ( ) ;
ASSERT ( accepted_socket ) ;
2020-02-16 07:20:54 +00:00
u8 address_buffer [ sizeof ( sockaddr_un ) ] ;
address_size = min ( sizeof ( sockaddr_un ) , static_cast < size_t > ( address_size ) ) ;
accepted_socket - > get_peer_address ( ( sockaddr * ) address_buffer , & address_size ) ;
copy_to_user ( user_address , address_buffer , address_size ) ;
copy_to_user ( user_address_size , & address_size ) ;
2019-08-11 13:38:20 +00:00
auto accepted_socket_description = FileDescription : : create ( * accepted_socket ) ;
2020-01-03 02:29:59 +00:00
accepted_socket_description - > set_readable ( true ) ;
accepted_socket_description - > set_writable ( true ) ;
2019-02-15 10:43:43 +00:00
// NOTE: The accepted socket inherits fd flags from the accepting socket.
// I'm not sure if this matches other systems but it makes sense to me.
2019-06-13 20:03:04 +00:00
accepted_socket_description - > set_blocking ( accepting_socket_description - > is_blocking ( ) ) ;
m_fds [ accepted_socket_fd ] . set ( move ( accepted_socket_description ) , m_fds [ accepting_socket_fd ] . flags ) ;
2019-10-08 19:41:46 +00:00
// NOTE: Moving this state to Completed is what causes connect() to unblock on the client side.
accepted_socket - > set_setup_state ( Socket : : SetupState : : Completed ) ;
2019-02-15 10:43:43 +00:00
return accepted_socket_fd ;
2019-02-14 13:17:38 +00:00
}
2020-02-28 12:20:26 +00:00
int Process : : sys $ connect ( int sockfd , const sockaddr * user_address , socklen_t user_address_size )
2019-02-14 13:17:38 +00:00
{
2020-02-28 12:20:26 +00:00
if ( ! validate_read ( user_address , user_address_size ) )
2019-02-14 14:55:19 +00:00
return - EFAULT ;
2019-04-06 12:54:32 +00:00
int fd = alloc_fd ( ) ;
if ( fd < 0 )
return fd ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-02-14 14:55:19 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-02-14 14:55:19 +00:00
return - ENOTSOCK ;
2019-08-11 13:38:20 +00:00
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
2020-02-28 12:20:26 +00:00
u8 address [ sizeof ( sockaddr_un ) ] ;
size_t address_size = min ( sizeof ( address ) , static_cast < size_t > ( user_address_size ) ) ;
copy_from_user ( address , user_address , address_size ) ;
return socket . connect ( * description , ( const sockaddr * ) address , address_size , description - > is_blocking ( ) ? ShouldBlock : : Yes : ShouldBlock : : No ) ;
2019-02-14 16:18:35 +00:00
}
2020-02-07 23:52:33 +00:00
int Process : : sys $ shutdown ( int sockfd , int how )
{
REQUIRE_PROMISE ( stdio ) ;
if ( how & ~ SHUT_RDWR )
return - EINVAL ;
auto description = file_description ( sockfd ) ;
if ( ! description )
return - EBADF ;
if ( ! description - > is_socket ( ) )
return - ENOTSOCK ;
auto & socket = * description - > socket ( ) ;
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
return socket . shutdown ( how ) ;
}
2020-01-11 11:47:47 +00:00
ssize_t Process : : sys $ sendto ( const Syscall : : SC_sendto_params * user_params )
2019-03-12 14:51:42 +00:00
{
2020-01-12 10:52:37 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-11 11:47:47 +00:00
Syscall : : SC_sendto_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-05 17:00:15 +00:00
2020-01-11 11:47:47 +00:00
int flags = params . flags ;
const sockaddr * addr = params . addr ;
socklen_t addr_length = params . addr_length ;
2019-03-12 14:51:42 +00:00
2020-01-11 11:47:47 +00:00
if ( ! validate ( params . data ) )
2019-03-12 14:51:42 +00:00
return - EFAULT ;
2019-03-13 22:14:30 +00:00
if ( addr & & ! validate_read ( addr , addr_length ) )
2019-03-12 14:51:42 +00:00
return - EFAULT ;
2020-01-11 11:47:47 +00:00
auto description = file_description ( params . sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-03-12 14:51:42 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-03-12 14:51:42 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-02-07 23:52:33 +00:00
if ( socket . is_shut_down_for_writing ( ) )
return - EPIPE ;
SmapDisabler disabler ;
2020-01-11 11:47:47 +00:00
return socket . sendto ( * description , params . data . data , params . data . size , flags , addr , addr_length ) ;
2019-03-12 14:51:42 +00:00
}
2020-01-11 11:47:47 +00:00
ssize_t Process : : sys $ recvfrom ( const Syscall : : SC_recvfrom_params * user_params )
2019-03-12 16:27:07 +00:00
{
2020-01-12 10:52:37 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-03-12 16:27:07 +00:00
2020-01-11 11:47:47 +00:00
Syscall : : SC_recvfrom_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2019-03-12 16:27:07 +00:00
2020-01-11 11:47:47 +00:00
int flags = params . flags ;
sockaddr * addr = params . addr ;
socklen_t * addr_length = params . addr_length ;
2020-01-11 12:03:44 +00:00
SmapDisabler disabler ;
2020-01-11 11:47:47 +00:00
if ( ! validate ( params . buffer ) )
2019-03-12 16:27:07 +00:00
return - EFAULT ;
2019-03-13 22:14:30 +00:00
if ( addr_length ) {
2019-05-04 01:27:50 +00:00
if ( ! validate_write_typed ( addr_length ) )
2019-03-13 22:14:30 +00:00
return - EFAULT ;
2019-05-04 01:27:50 +00:00
if ( ! validate_write ( addr , * addr_length ) )
2019-03-13 22:14:30 +00:00
return - EFAULT ;
} else if ( addr ) {
2019-06-07 09:43:58 +00:00
return - EINVAL ;
2019-03-13 22:14:30 +00:00
}
2020-01-11 11:47:47 +00:00
auto description = file_description ( params . sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-03-12 16:27:07 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-03-12 16:27:07 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2019-05-20 01:44:45 +00:00
2020-02-07 23:52:33 +00:00
if ( socket . is_shut_down_for_reading ( ) )
return 0 ;
2019-06-13 20:03:04 +00:00
bool original_blocking = description - > is_blocking ( ) ;
2019-05-20 01:44:45 +00:00
if ( flags & MSG_DONTWAIT )
2019-06-13 20:03:04 +00:00
description - > set_blocking ( false ) ;
2019-05-20 01:44:45 +00:00
2020-01-11 11:47:47 +00:00
auto nrecv = socket . recvfrom ( * description , params . buffer . data , params . buffer . size , flags , addr , addr_length ) ;
2019-05-20 01:44:45 +00:00
if ( flags & MSG_DONTWAIT )
2019-06-13 20:03:04 +00:00
description - > set_blocking ( original_blocking ) ;
2019-05-20 01:44:45 +00:00
return nrecv ;
2019-03-12 16:27:07 +00:00
}
2020-01-27 20:11:36 +00:00
template < bool sockname , typename Params >
int Process : : get_sock_or_peer_name ( const Params & params )
2019-05-19 17:55:27 +00:00
{
2020-01-27 20:11:36 +00:00
socklen_t addrlen_value ;
if ( ! validate_read_and_copy_typed ( & addrlen_value , params . addrlen ) )
2019-05-19 17:55:27 +00:00
return - EFAULT ;
2020-01-27 20:11:36 +00:00
if ( addrlen_value < = 0 )
2019-05-19 17:55:27 +00:00
return - EINVAL ;
2020-01-27 20:11:36 +00:00
if ( ! validate_write ( params . addr , addrlen_value ) )
2019-05-19 17:55:27 +00:00
return - EFAULT ;
2020-01-27 20:11:36 +00:00
if ( ! validate_write_typed ( params . addrlen ) )
return - EFAULT ;
auto description = file_description ( params . sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-05-19 17:55:27 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-05-19 17:55:27 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
2019-05-20 18:33:03 +00:00
2020-01-27 20:11:36 +00:00
u8 address_buffer [ sizeof ( sockaddr_un ) ] ;
addrlen_value = min ( sizeof ( sockaddr_un ) , static_cast < size_t > ( addrlen_value ) ) ;
2020-02-07 22:42:28 +00:00
if constexpr ( sockname )
socket . get_local_address ( ( sockaddr * ) address_buffer , & addrlen_value ) ;
else
socket . get_peer_address ( ( sockaddr * ) address_buffer , & addrlen_value ) ;
2020-01-27 20:11:36 +00:00
copy_to_user ( params . addr , address_buffer , addrlen_value ) ;
2020-02-07 22:42:28 +00:00
copy_to_user ( params . addrlen , & addrlen_value ) ;
2019-05-20 18:33:03 +00:00
return 0 ;
}
2020-01-27 20:11:36 +00:00
int Process : : sys $ getsockname ( const Syscall : : SC_getsockname_params * user_params )
2019-05-20 18:33:03 +00:00
{
2020-01-27 20:11:36 +00:00
Syscall : : SC_getsockname_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
2019-05-20 18:33:03 +00:00
return - EFAULT ;
2020-01-27 20:11:36 +00:00
return get_sock_or_peer_name < true > ( params ) ;
}
2019-05-20 18:33:03 +00:00
2020-01-27 20:11:36 +00:00
int Process : : sys $ getpeername ( const Syscall : : SC_getpeername_params * user_params )
{
Syscall : : SC_getpeername_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
2019-05-20 18:33:03 +00:00
return - EFAULT ;
2020-01-27 20:11:36 +00:00
return get_sock_or_peer_name < false > ( params ) ;
2019-05-19 17:55:27 +00:00
}
2020-01-26 08:58:58 +00:00
int Process : : sys $ sched_setparam ( int tid , const struct sched_param * param )
2019-05-29 21:20:51 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2019-05-29 21:20:51 +00:00
if ( ! validate_read_typed ( param ) )
return - EFAULT ;
2020-01-05 17:00:15 +00:00
int desired_priority ;
2020-01-20 12:38:31 +00:00
copy_from_user ( & desired_priority , & param - > sched_priority ) ;
2020-01-05 17:00:15 +00:00
2019-05-29 21:20:51 +00:00
InterruptDisabler disabler ;
2020-02-17 14:04:27 +00:00
auto * peer = Thread : : current ;
2020-01-26 08:58:58 +00:00
if ( tid ! = 0 )
peer = Thread : : from_tid ( tid ) ;
2019-05-29 21:20:51 +00:00
if ( ! peer )
return - ESRCH ;
2020-01-26 08:58:58 +00:00
if ( ! is_superuser ( ) & & m_euid ! = peer - > process ( ) . m_uid & & m_uid ! = peer - > process ( ) . m_uid )
2019-05-29 21:20:51 +00:00
return - EPERM ;
2020-01-05 17:00:15 +00:00
if ( desired_priority < THREAD_PRIORITY_MIN | | desired_priority > THREAD_PRIORITY_MAX )
2019-05-29 21:20:51 +00:00
return - EINVAL ;
2020-01-26 08:58:58 +00:00
peer - > set_priority ( ( u32 ) desired_priority ) ;
2019-05-29 21:20:51 +00:00
return 0 ;
}
int Process : : sys $ sched_getparam ( pid_t pid , struct sched_param * param )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2020-01-05 17:00:15 +00:00
if ( ! validate_write_typed ( param ) )
2019-05-29 21:20:51 +00:00
return - EFAULT ;
InterruptDisabler disabler ;
2020-02-17 14:04:27 +00:00
auto * peer = Thread : : current ;
2019-05-29 21:20:51 +00:00
if ( pid ! = 0 )
2020-01-26 08:58:58 +00:00
peer = Thread : : from_tid ( pid ) ;
2019-05-29 21:20:51 +00:00
if ( ! peer )
return - ESRCH ;
2020-01-26 08:58:58 +00:00
if ( ! is_superuser ( ) & & m_euid ! = peer - > process ( ) . m_uid & & m_uid ! = peer - > process ( ) . m_uid )
2019-05-29 21:20:51 +00:00
return - EPERM ;
2020-01-26 08:58:58 +00:00
int priority = peer - > priority ( ) ;
2020-01-20 12:38:31 +00:00
copy_to_user ( & param - > sched_priority , & priority ) ;
2019-05-29 21:20:51 +00:00
return 0 ;
}
2019-03-13 12:13:23 +00:00
int Process : : sys $ getsockopt ( const Syscall : : SC_getsockopt_params * params )
{
if ( ! validate_read_typed ( params ) )
return - EFAULT ;
2019-11-10 14:32:59 +00:00
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2020-01-05 08:13:00 +00:00
int sockfd = params - > sockfd ;
int level = params - > level ;
int option = params - > option ;
void * value = params - > value ;
socklen_t * value_size = params - > value_size ;
2019-03-13 12:13:23 +00:00
if ( ! validate_write_typed ( value_size ) )
return - EFAULT ;
if ( ! validate_write ( value , * value_size ) )
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-03-13 12:13:23 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-03-13 12:13:23 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-01-17 10:12:06 +00:00
if ( has_promised ( Pledge : : accept ) & & socket . is_local ( ) & & level = = SOL_SOCKET & & option = = SO_PEERCRED ) {
// We make an exception for SOL_SOCKET::SO_PEERCRED on local sockets if you've pledged "accept"
} else {
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
}
2019-12-06 17:38:36 +00:00
return socket . getsockopt ( * description , level , option , value , value_size ) ;
2019-03-13 12:13:23 +00:00
}
int Process : : sys $ setsockopt ( const Syscall : : SC_setsockopt_params * params )
{
if ( ! validate_read_typed ( params ) )
return - EFAULT ;
2019-11-10 14:32:59 +00:00
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2020-01-05 08:13:00 +00:00
int sockfd = params - > sockfd ;
int level = params - > level ;
int option = params - > option ;
const void * value = params - > value ;
socklen_t value_size = params - > value_size ;
2019-03-13 12:13:23 +00:00
if ( ! validate_read ( value , value_size ) )
return - EFAULT ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( sockfd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-03-13 12:13:23 +00:00
return - EBADF ;
2019-06-13 20:03:04 +00:00
if ( ! description - > is_socket ( ) )
2019-03-13 12:13:23 +00:00
return - ENOTSOCK ;
2019-06-13 20:03:04 +00:00
auto & socket = * description - > socket ( ) ;
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE_FOR_SOCKET_DOMAIN ( socket . domain ( ) ) ;
2019-03-13 12:13:23 +00:00
return socket . setsockopt ( level , option , value , value_size ) ;
}
2019-02-16 11:13:43 +00:00
void Process : : disown_all_shared_buffers ( )
{
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2019-04-20 12:02:19 +00:00
Vector < SharedBuffer * , 32 > buffers_to_disown ;
2019-02-20 20:31:52 +00:00
for ( auto & it : shared_buffers ( ) . resource ( ) )
buffers_to_disown . append ( it . value . ptr ( ) ) ;
for ( auto * shared_buffer : buffers_to_disown )
shared_buffer - > disown ( m_pid ) ;
2019-02-16 11:13:43 +00:00
}
2020-02-28 10:45:19 +00:00
int Process : : sys $ shbuf_create ( int size , void * * buffer )
2019-02-16 11:13:43 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-03-08 11:22:55 +00:00
if ( ! size | | size < 0 )
2019-02-20 20:31:52 +00:00
return - EINVAL ;
size = PAGE_ROUND_UP ( size ) ;
2019-02-16 11:13:43 +00:00
if ( ! validate_write_typed ( buffer ) )
return - EFAULT ;
2019-07-15 18:38:41 +00:00
2019-02-16 11:13:43 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
static int s_next_shbuf_id ;
int shbuf_id = + + s_next_shbuf_id ;
auto shared_buffer = make < SharedBuffer > ( shbuf_id , size ) ;
2019-07-15 18:38:41 +00:00
shared_buffer - > share_with ( m_pid ) ;
2020-01-05 17:00:15 +00:00
void * address = shared_buffer - > ref_for_process_and_get_address ( * this ) ;
2020-02-18 13:12:39 +00:00
copy_to_user ( buffer , & address ) ;
2019-06-22 14:22:34 +00:00
ASSERT ( ( int ) shared_buffer - > size ( ) > = size ) ;
2019-02-16 23:13:47 +00:00
# ifdef SHARED_BUFFER_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Created shared buffer " < < shbuf_id < < " @ " < < buffer < < " ( " < < size < < " bytes, vmobject is " < < shared_buffer - > size ( ) < < " ) " ;
2019-02-16 23:13:47 +00:00
# endif
2020-02-28 10:45:19 +00:00
shared_buffers ( ) . resource ( ) . set ( shbuf_id , move ( shared_buffer ) ) ;
2019-07-15 18:38:41 +00:00
2020-02-28 10:45:19 +00:00
return shbuf_id ;
2019-02-16 11:13:43 +00:00
}
2020-02-28 10:45:19 +00:00
int Process : : sys $ shbuf_allow_pid ( int shbuf_id , pid_t peer_pid )
2019-07-18 07:52:22 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-07-18 07:52:22 +00:00
if ( ! peer_pid | | peer_pid < 0 | | peer_pid = = m_pid )
return - EINVAL ;
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
auto it = shared_buffers ( ) . resource ( ) . find ( shbuf_id ) ;
2019-07-18 07:52:22 +00:00
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
2019-07-28 05:11:14 +00:00
if ( ! shared_buffer . is_shared_with ( m_pid ) )
return - EPERM ;
2019-07-18 07:52:22 +00:00
{
InterruptDisabler disabler ;
auto * peer = Process : : from_pid ( peer_pid ) ;
if ( ! peer )
return - ESRCH ;
}
shared_buffer . share_with ( peer_pid ) ;
return 0 ;
}
2020-02-28 10:45:19 +00:00
int Process : : sys $ shbuf_allow_all ( int shbuf_id )
2019-07-29 05:26:01 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-07-29 05:26:01 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
auto it = shared_buffers ( ) . resource ( ) . find ( shbuf_id ) ;
2019-07-29 05:26:01 +00:00
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
if ( ! shared_buffer . is_shared_with ( m_pid ) )
return - EPERM ;
shared_buffer . share_globally ( ) ;
return 0 ;
}
2020-02-28 10:45:19 +00:00
int Process : : sys $ shbuf_release ( int shbuf_id )
2019-02-16 11:13:43 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-02-16 11:13:43 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
auto it = shared_buffers ( ) . resource ( ) . find ( shbuf_id ) ;
2019-02-16 11:13:43 +00:00
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
2019-07-28 05:11:14 +00:00
if ( ! shared_buffer . is_shared_with ( m_pid ) )
return - EPERM ;
2019-02-16 23:13:47 +00:00
# ifdef SHARED_BUFFER_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Releasing shared buffer " < < shbuf_id < < " , buffer count: " < < shared_buffers ( ) . resource ( ) . size ( ) ;
2019-02-16 23:13:47 +00:00
# endif
2019-07-19 15:46:21 +00:00
shared_buffer . deref_for_process ( * this ) ;
2019-02-16 11:13:43 +00:00
return 0 ;
}
2020-02-28 11:29:14 +00:00
void * Process : : sys $ shbuf_get ( int shbuf_id , size_t * user_size )
2019-02-16 11:13:43 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2020-02-28 11:29:14 +00:00
if ( user_size & & ! validate_write_typed ( user_size ) )
return ( void * ) - EFAULT ;
2019-02-16 11:13:43 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
auto it = shared_buffers ( ) . resource ( ) . find ( shbuf_id ) ;
2019-02-16 11:13:43 +00:00
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return ( void * ) - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
2019-07-15 18:38:41 +00:00
if ( ! shared_buffer . is_shared_with ( m_pid ) )
2019-07-28 05:11:14 +00:00
return ( void * ) - EPERM ;
2019-02-16 23:13:47 +00:00
# ifdef SHARED_BUFFER_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Retaining shared buffer " < < shbuf_id < < " , buffer count: " < < shared_buffers ( ) . resource ( ) . size ( ) ;
2019-02-16 23:13:47 +00:00
# endif
2020-02-28 11:29:14 +00:00
if ( user_size ) {
size_t size = shared_buffer . size ( ) ;
copy_to_user ( user_size , & size ) ;
}
2019-07-19 15:46:21 +00:00
return shared_buffer . ref_for_process_and_get_address ( * this ) ;
2019-02-16 11:13:43 +00:00
}
2019-03-05 09:34:08 +00:00
2020-02-28 10:45:19 +00:00
int Process : : sys $ shbuf_seal ( int shbuf_id )
2019-03-08 11:22:55 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-03-08 11:22:55 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
auto it = shared_buffers ( ) . resource ( ) . find ( shbuf_id ) ;
2019-03-08 11:22:55 +00:00
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
2019-07-15 18:38:41 +00:00
if ( ! shared_buffer . is_shared_with ( m_pid ) )
2019-07-28 05:11:14 +00:00
return - EPERM ;
2019-03-08 11:22:55 +00:00
# ifdef SHARED_BUFFER_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Sealing shared buffer " < < shbuf_id ;
2019-03-08 11:22:55 +00:00
# endif
shared_buffer . seal ( ) ;
return 0 ;
}
2020-02-28 10:45:19 +00:00
int Process : : sys $ shbuf_set_volatile ( int shbuf_id , bool state )
2019-12-09 19:06:47 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-12-09 19:06:47 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
2020-02-28 10:45:19 +00:00
auto it = shared_buffers ( ) . resource ( ) . find ( shbuf_id ) ;
2019-12-09 19:06:47 +00:00
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
if ( ! shared_buffer . is_shared_with ( m_pid ) )
return - EPERM ;
# ifdef SHARED_BUFFER_DEBUG
2020-03-01 19:45:39 +00:00
klog ( ) < < " Set shared buffer " < < shbuf_id < < " volatile: " < < state ;
2019-12-09 19:06:47 +00:00
# endif
if ( ! state ) {
bool was_purged = shared_buffer . vmobject ( ) . was_purged ( ) ;
shared_buffer . vmobject ( ) . set_volatile ( state ) ;
shared_buffer . vmobject ( ) . set_was_purged ( false ) ;
return was_purged ? 1 : 0 ;
}
shared_buffer . vmobject ( ) . set_volatile ( true ) ;
return 0 ;
}
2019-07-03 19:17:35 +00:00
void Process : : terminate_due_to_signal ( u8 signal )
2019-03-23 21:03:17 +00:00
{
ASSERT_INTERRUPTS_DISABLED ( ) ;
ASSERT ( signal < 32 ) ;
2020-02-29 11:51:44 +00:00
dbg ( ) < < " Terminating due to signal " < < signal ;
2019-03-23 21:03:17 +00:00
m_termination_status = 0 ;
m_termination_signal = signal ;
die ( ) ;
}
2019-07-03 19:17:35 +00:00
void Process : : send_signal ( u8 signal , Process * sender )
2019-03-23 21:03:17 +00:00
{
2020-01-06 13:37:08 +00:00
InterruptDisabler disabler ;
2020-02-27 10:03:21 +00:00
if ( ! m_thread_count )
return ;
2020-01-06 13:37:08 +00:00
auto * thread = Thread : : from_tid ( m_pid ) ;
if ( ! thread )
thread = & any_thread ( ) ;
thread - > send_signal ( signal , sender ) ;
2019-03-23 21:03:17 +00:00
}
2019-03-23 21:59:08 +00:00
2020-01-12 10:51:31 +00:00
int Process : : sys $ create_thread ( void * ( * entry ) ( void * ) , void * argument , const Syscall : : SC_create_thread_params * user_params )
2019-03-23 21:59:08 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( thread ) ;
2019-03-23 21:59:08 +00:00
if ( ! validate_read ( ( const void * ) entry , sizeof ( void * ) ) )
return - EFAULT ;
2019-11-18 03:08:10 +00:00
2020-01-12 10:51:31 +00:00
Syscall : : SC_create_thread_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-12 10:51:31 +00:00
unsigned detach_state = params . m_detach_state ;
int schedule_priority = params . m_schedule_priority ;
void * stack_location = params . m_stack_location ;
unsigned stack_size = params . m_stack_size ;
2020-01-05 17:00:15 +00:00
2020-01-05 08:13:00 +00:00
if ( ! validate_write ( stack_location , stack_size ) )
return - EFAULT ;
u32 user_stack_address = reinterpret_cast < u32 > ( stack_location ) + stack_size ;
2019-11-18 03:08:10 +00:00
if ( ! MM . validate_user_stack ( * this , VirtualAddress ( user_stack_address - 4 ) ) )
2019-11-17 16:28:17 +00:00
return - EFAULT ;
2019-11-18 03:08:10 +00:00
// FIXME: return EAGAIN if Thread::all_threads().size() is greater than PTHREAD_THREADS_MAX
2020-01-05 08:13:00 +00:00
int requested_thread_priority = schedule_priority ;
2019-12-30 17:46:17 +00:00
if ( requested_thread_priority < THREAD_PRIORITY_MIN | | requested_thread_priority > THREAD_PRIORITY_MAX )
2019-11-18 03:08:10 +00:00
return - EINVAL ;
2020-01-05 08:13:00 +00:00
bool is_thread_joinable = ( 0 = = detach_state ) ;
2019-11-18 03:08:10 +00:00
// FIXME: Do something with guard pages?
2019-03-23 21:59:08 +00:00
auto * thread = new Thread ( * this ) ;
2019-11-18 03:08:10 +00:00
2019-12-07 19:45:26 +00:00
// We know this thread is not the main_thread,
// So give it a unique name until the user calls $set_thread_name on it
// length + 4 to give space for our extra junk at the end
StringBuilder builder ( m_name . length ( ) + 4 ) ;
builder . append ( m_name ) ;
builder . appendf ( " [%d] " , thread - > tid ( ) ) ;
thread - > set_name ( builder . to_string ( ) ) ;
2019-11-18 03:08:10 +00:00
thread - > set_priority ( requested_thread_priority ) ;
thread - > set_joinable ( is_thread_joinable ) ;
2019-03-23 21:59:08 +00:00
auto & tss = thread - > tss ( ) ;
2020-03-08 09:36:51 +00:00
tss . eip = ( FlatPtr ) entry ;
2019-03-23 21:59:08 +00:00
tss . eflags = 0x0202 ;
tss . cr3 = page_directory ( ) . cr3 ( ) ;
2019-11-18 03:08:10 +00:00
tss . esp = user_stack_address ;
2019-11-17 16:28:17 +00:00
// NOTE: The stack needs to be 16-byte aligned.
2020-03-08 09:36:51 +00:00
thread - > push_value_on_stack ( ( FlatPtr ) argument ) ;
2019-11-17 16:28:17 +00:00
thread - > push_value_on_stack ( 0 ) ;
2019-09-07 13:50:44 +00:00
thread - > make_thread_specific_region ( { } ) ;
2019-03-23 21:59:08 +00:00
thread - > set_state ( Thread : : State : : Runnable ) ;
2019-07-14 08:17:58 +00:00
return thread - > tid ( ) ;
2019-03-23 21:59:08 +00:00
}
2019-03-25 12:03:49 +00:00
2019-11-14 19:58:23 +00:00
void Process : : sys $ exit_thread ( void * exit_value )
2019-04-29 13:17:20 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( thread ) ;
2019-04-29 13:56:25 +00:00
cli ( ) ;
2020-02-17 14:04:27 +00:00
Thread : : current - > m_exit_value = exit_value ;
Thread : : current - > set_should_die ( ) ;
2020-01-12 21:53:20 +00:00
big_lock ( ) . force_unlock_if_locked ( ) ;
2020-02-17 14:04:27 +00:00
Thread : : current - > die_if_needed ( ) ;
2019-04-29 13:17:20 +00:00
ASSERT_NOT_REACHED ( ) ;
}
2019-12-07 13:47:00 +00:00
int Process : : sys $ detach_thread ( int tid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( thread ) ;
2020-01-27 12:48:54 +00:00
InterruptDisabler disabler ;
2020-01-04 17:56:04 +00:00
auto * thread = Thread : : from_tid ( tid ) ;
if ( ! thread | | thread - > pid ( ) ! = pid ( ) )
2019-12-07 13:47:00 +00:00
return - ESRCH ;
if ( ! thread - > is_joinable ( ) )
return - EINVAL ;
thread - > set_joinable ( false ) ;
return 0 ;
}
2019-11-14 19:58:23 +00:00
int Process : : sys $ join_thread ( int tid , void * * exit_value )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( thread ) ;
2019-11-14 19:58:23 +00:00
if ( exit_value & & ! validate_write_typed ( exit_value ) )
return - EFAULT ;
2020-01-27 12:48:54 +00:00
InterruptDisabler disabler ;
2020-01-04 17:56:04 +00:00
auto * thread = Thread : : from_tid ( tid ) ;
if ( ! thread | | thread - > pid ( ) ! = pid ( ) )
2019-11-14 19:58:23 +00:00
return - ESRCH ;
2020-02-17 14:04:27 +00:00
if ( thread = = Thread : : current )
2019-11-14 19:58:23 +00:00
return - EDEADLK ;
2020-02-17 14:04:27 +00:00
if ( thread - > m_joinee = = Thread : : current )
2019-11-14 19:58:23 +00:00
return - EDEADLK ;
2020-02-17 14:04:27 +00:00
ASSERT ( thread - > m_joiner ! = Thread : : current ) ;
2019-11-14 19:58:23 +00:00
if ( thread - > m_joiner )
return - EINVAL ;
2019-11-18 03:08:10 +00:00
if ( ! thread - > is_joinable ( ) )
return - EINVAL ;
2019-11-14 19:58:23 +00:00
2019-11-14 20:04:34 +00:00
void * joinee_exit_value = nullptr ;
2020-01-10 18:15:01 +00:00
// NOTE: pthread_join() cannot be interrupted by signals. Only by death.
for ( ; ; ) {
2020-02-17 14:04:27 +00:00
auto result = Thread : : current - > block < Thread : : JoinBlocker > ( * thread , joinee_exit_value ) ;
2020-01-10 18:15:01 +00:00
if ( result = = Thread : : BlockResult : : InterruptedByDeath ) {
// NOTE: This cleans things up so that Thread::finalize() won't
// get confused about a missing joiner when finalizing the joinee.
2020-02-27 06:40:40 +00:00
InterruptDisabler disabler_t ;
if ( Thread : : current - > m_joinee ) {
Thread : : current - > m_joinee - > m_joiner = nullptr ;
Thread : : current - > m_joinee = nullptr ;
}
break ;
2020-01-10 18:15:01 +00:00
}
}
2019-11-14 19:58:23 +00:00
// NOTE: 'thread' is very possibly deleted at this point. Clear it just to be safe.
thread = nullptr ;
if ( exit_value )
2020-01-20 12:38:31 +00:00
copy_to_user ( exit_value , & joinee_exit_value ) ;
2019-11-14 19:58:23 +00:00
return 0 ;
}
2020-01-11 11:47:47 +00:00
int Process : : sys $ set_thread_name ( int tid , const char * user_name , size_t user_name_length )
2019-12-07 19:45:26 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( thread ) ;
2020-01-11 11:47:47 +00:00
auto name = validate_and_copy_string_from_user ( user_name , user_name_length ) ;
if ( name . is_null ( ) )
2019-12-07 19:45:26 +00:00
return - EFAULT ;
const size_t max_thread_name_size = 64 ;
2020-01-05 20:51:06 +00:00
if ( name . length ( ) > max_thread_name_size )
2019-12-07 19:45:26 +00:00
return - EINVAL ;
2020-01-27 12:48:54 +00:00
InterruptDisabler disabler ;
2020-01-04 17:56:04 +00:00
auto * thread = Thread : : from_tid ( tid ) ;
if ( ! thread | | thread - > pid ( ) ! = pid ( ) )
2019-12-07 19:45:26 +00:00
return - ESRCH ;
2020-01-05 20:51:06 +00:00
thread - > set_name ( name ) ;
2019-12-07 19:45:26 +00:00
return 0 ;
}
2020-01-11 11:47:47 +00:00
int Process : : sys $ get_thread_name ( int tid , char * buffer , size_t buffer_size )
2019-12-07 19:45:26 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( thread ) ;
2020-01-11 11:47:47 +00:00
if ( buffer_size = = 0 )
2019-12-07 19:45:26 +00:00
return - EINVAL ;
if ( ! validate_write ( buffer , buffer_size ) )
return - EFAULT ;
2020-01-27 12:48:54 +00:00
InterruptDisabler disabler ;
2020-01-04 17:56:04 +00:00
auto * thread = Thread : : from_tid ( tid ) ;
if ( ! thread | | thread - > pid ( ) ! = pid ( ) )
2019-12-07 19:45:26 +00:00
return - ESRCH ;
2020-01-05 17:00:15 +00:00
if ( thread - > name ( ) . length ( ) + 1 > ( size_t ) buffer_size )
2019-12-07 19:45:26 +00:00
return - ENAMETOOLONG ;
2020-01-05 17:00:15 +00:00
copy_to_user ( buffer , thread - > name ( ) . characters ( ) , thread - > name ( ) . length ( ) + 1 ) ;
2019-12-07 19:45:26 +00:00
return 0 ;
}
2019-03-25 12:03:49 +00:00
int Process : : sys $ gettid ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-02-17 14:04:27 +00:00
return Thread : : current - > tid ( ) ;
2019-03-25 12:03:49 +00:00
}
int Process : : sys $ donate ( int tid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-03-25 12:03:49 +00:00
if ( tid < 0 )
return - EINVAL ;
InterruptDisabler disabler ;
2020-01-04 17:56:04 +00:00
auto * thread = Thread : : from_tid ( tid ) ;
if ( ! thread | | thread - > pid ( ) ! = pid ( ) )
return - ESRCH ;
Scheduler : : donate_to ( thread , " sys$donate " ) ;
2019-03-25 12:03:49 +00:00
return 0 ;
}
2019-04-07 21:35:26 +00:00
2020-01-11 09:36:54 +00:00
int Process : : sys $ rename ( const Syscall : : SC_rename_params * user_params )
2019-04-07 21:35:26 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( cpath ) ;
2020-01-11 09:36:54 +00:00
Syscall : : SC_rename_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-11 11:47:47 +00:00
auto old_path = get_syscall_path_argument ( params . old_path ) ;
2020-01-11 09:36:54 +00:00
if ( old_path . is_error ( ) )
return old_path . error ( ) ;
2020-01-11 11:47:47 +00:00
auto new_path = get_syscall_path_argument ( params . new_path ) ;
2020-01-11 09:36:54 +00:00
if ( new_path . is_error ( ) )
return new_path . error ( ) ;
return VFS : : the ( ) . rename ( old_path . value ( ) , new_path . value ( ) , current_directory ( ) ) ;
2019-04-07 21:35:26 +00:00
}
2019-04-08 21:44:12 +00:00
2019-04-08 23:10:00 +00:00
int Process : : sys $ ftruncate ( int fd , off_t length )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-07 13:42:56 +00:00
if ( length < 0 )
return - EINVAL ;
2020-01-07 14:53:42 +00:00
auto description = file_description ( fd ) ;
2019-06-13 20:03:04 +00:00
if ( ! description )
2019-04-08 23:10:00 +00:00
return - EBADF ;
2020-01-07 13:42:56 +00:00
if ( ! description - > is_writable ( ) )
return - EBADF ;
2020-02-08 11:07:04 +00:00
return description - > truncate ( static_cast < u64 > ( length ) ) ;
2019-04-08 23:10:00 +00:00
}
2019-04-22 16:44:45 +00:00
2020-01-06 10:12:29 +00:00
int Process : : sys $ watch_file ( const char * user_path , size_t path_length )
2019-07-22 18:01:11 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( rpath ) ;
2020-01-06 10:10:38 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
2020-01-05 17:00:15 +00:00
2020-01-06 10:10:38 +00:00
auto custody_or_error = VFS : : the ( ) . resolve_path ( path . value ( ) , current_directory ( ) ) ;
2019-07-22 18:01:11 +00:00
if ( custody_or_error . is_error ( ) )
return custody_or_error . error ( ) ;
auto & custody = custody_or_error . value ( ) ;
auto & inode = custody - > inode ( ) ;
2019-12-15 18:33:39 +00:00
if ( ! inode . fs ( ) . supports_watchers ( ) )
return - ENOTSUP ;
2019-07-22 18:01:11 +00:00
int fd = alloc_fd ( ) ;
if ( fd < 0 )
return fd ;
m_fds [ fd ] . set ( FileDescription : : create ( * InodeWatcher : : create ( inode ) ) ) ;
2020-01-03 02:29:59 +00:00
m_fds [ fd ] . description - > set_readable ( true ) ;
2019-07-22 18:01:11 +00:00
return fd ;
}
2019-07-19 11:08:26 +00:00
int Process : : sys $ halt ( )
{
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-02-24 22:13:33 +00:00
dbg ( ) < < " acquiring FS locks... " ;
2019-07-19 11:08:26 +00:00
FS : : lock_all ( ) ;
2020-02-24 22:13:33 +00:00
dbg ( ) < < " syncing mounted filesystems... " ;
2019-07-19 11:08:26 +00:00
FS : : sync ( ) ;
2020-02-24 22:13:33 +00:00
dbg ( ) < < " attempting system shutdown... " ;
2019-07-19 11:08:26 +00:00
IO : : out16 ( 0x604 , 0x2000 ) ;
2020-04-10 10:58:27 +00:00
return 0 ;
2019-07-19 11:08:26 +00:00
}
2019-07-19 07:58:12 +00:00
int Process : : sys $ reboot ( )
{
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-02-24 22:13:33 +00:00
dbg ( ) < < " acquiring FS locks... " ;
2019-07-19 07:58:12 +00:00
FS : : lock_all ( ) ;
2020-02-24 22:13:33 +00:00
dbg ( ) < < " syncing mounted filesystems... " ;
2019-07-19 07:58:12 +00:00
FS : : sync ( ) ;
2020-03-08 14:50:46 +00:00
dbg ( ) < < " attempting reboot via ACPI " ;
2020-04-09 12:31:47 +00:00
if ( ACPI : : is_enabled ( ) )
ACPI : : Parser : : the ( ) - > try_acpi_reboot ( ) ;
2020-02-24 22:13:33 +00:00
dbg ( ) < < " attempting reboot via KB Controller... " ;
2019-07-19 07:58:12 +00:00
IO : : out8 ( 0x64 , 0xFE ) ;
2020-04-10 10:58:27 +00:00
return 0 ;
2019-07-19 07:58:12 +00:00
}
2020-01-11 09:46:21 +00:00
int Process : : sys $ mount ( const Syscall : : SC_mount_params * user_params )
2019-08-02 13:18:47 +00:00
{
2019-08-02 17:03:50 +00:00
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-01-11 09:46:21 +00:00
Syscall : : SC_mount_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-05 17:00:15 +00:00
2020-04-06 08:55:08 +00:00
auto source_fd = params . source_fd ;
2020-01-11 09:46:21 +00:00
auto target = validate_and_copy_string_from_user ( params . target ) ;
auto fs_type = validate_and_copy_string_from_user ( params . fs_type ) ;
2020-04-06 08:55:08 +00:00
if ( target . is_null ( ) | | fs_type . is_null ( ) )
2019-08-02 13:18:47 +00:00
return - EFAULT ;
2020-04-06 08:55:08 +00:00
auto description = file_description ( source_fd ) ;
if ( ! description . is_null ( ) )
dbg ( ) < < " mount " < < fs_type < < " : source fd " < < source_fd < < " @ " < < target ;
else
dbg ( ) < < " mount " < < fs_type < < " @ " < < target ;
2019-08-02 17:03:50 +00:00
2020-01-11 09:46:21 +00:00
auto custody_or_error = VFS : : the ( ) . resolve_path ( target , current_directory ( ) ) ;
2019-08-02 17:03:50 +00:00
if ( custody_or_error . is_error ( ) )
return custody_or_error . error ( ) ;
2020-01-11 09:46:21 +00:00
auto & target_custody = custody_or_error . value ( ) ;
2019-08-02 17:03:50 +00:00
2020-01-11 09:46:21 +00:00
RefPtr < FS > fs ;
2019-08-02 13:18:47 +00:00
2020-01-11 16:08:35 +00:00
if ( params . flags & MS_BIND ) {
// We're doing a bind mount.
2020-04-06 08:55:08 +00:00
if ( description . is_null ( ) )
return - EBADF ;
ASSERT ( description - > custody ( ) ) ;
return VFS : : the ( ) . bind_mount ( * description - > custody ( ) , target_custody , params . flags ) ;
2020-01-11 16:08:35 +00:00
}
2020-01-11 09:46:21 +00:00
if ( fs_type = = " ext2 " | | fs_type = = " Ext2FS " ) {
2020-04-06 08:55:08 +00:00
if ( description . is_null ( ) )
return - EBADF ;
ASSERT ( description - > custody ( ) ) ;
if ( ! description - > file ( ) . is_seekable ( ) ) {
dbg ( ) < < " mount: this is not a seekable file " ;
2019-08-15 16:13:56 +00:00
return - ENODEV ;
}
2019-08-02 17:30:30 +00:00
2020-04-06 08:55:08 +00:00
dbg ( ) < < " mount: attempting to mount " < < description - > absolute_path ( ) < < " on " < < target ;
2019-08-15 16:13:56 +00:00
2020-04-06 08:55:08 +00:00
fs = Ext2FS : : create ( * description ) ;
2020-01-11 09:46:21 +00:00
} else if ( fs_type = = " proc " | | fs_type = = " ProcFS " ) {
2019-08-15 16:13:56 +00:00
fs = ProcFS : : create ( ) ;
2020-01-11 09:46:21 +00:00
} else if ( fs_type = = " devpts " | | fs_type = = " DevPtsFS " ) {
2019-08-15 16:13:56 +00:00
fs = DevPtsFS : : create ( ) ;
2020-01-11 09:46:21 +00:00
} else if ( fs_type = = " tmp " | | fs_type = = " TmpFS " ) {
2019-08-15 16:13:56 +00:00
fs = TmpFS : : create ( ) ;
2020-01-11 09:46:21 +00:00
} else {
2019-08-15 16:13:56 +00:00
return - ENODEV ;
2020-01-11 09:46:21 +00:00
}
2019-08-02 13:18:47 +00:00
2019-08-15 16:13:56 +00:00
if ( ! fs - > initialize ( ) ) {
2020-04-06 08:55:08 +00:00
dbg ( ) < < " mount: failed to initialize " < < fs_type < < " filesystem, fd - " < < source_fd ;
2019-08-02 17:30:30 +00:00
return - ENODEV ;
2019-08-02 13:18:47 +00:00
}
2020-01-11 15:25:26 +00:00
auto result = VFS : : the ( ) . mount ( fs . release_nonnull ( ) , target_custody , params . flags ) ;
2020-04-06 08:55:08 +00:00
if ( ! description . is_null ( ) )
dbg ( ) < < " mount: successfully mounted " < < description - > absolute_path ( ) < < " on " < < target ;
else
dbg ( ) < < " mount: successfully mounted " < < target ;
2019-08-02 17:03:50 +00:00
return result ;
2019-08-02 13:18:47 +00:00
}
2020-01-09 11:41:15 +00:00
int Process : : sys $ umount ( const char * user_mountpoint , size_t mountpoint_length )
2019-08-11 13:56:39 +00:00
{
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-01-09 11:41:15 +00:00
if ( ! validate_read ( user_mountpoint , mountpoint_length ) )
2019-08-11 13:56:39 +00:00
return - EFAULT ;
2020-01-09 11:41:15 +00:00
auto mountpoint = get_syscall_path_argument ( user_mountpoint , mountpoint_length ) ;
if ( mountpoint . is_error ( ) )
return mountpoint . error ( ) ;
auto metadata_or_error = VFS : : the ( ) . lookup_metadata ( mountpoint . value ( ) , current_directory ( ) ) ;
2019-08-11 13:56:39 +00:00
if ( metadata_or_error . is_error ( ) )
return metadata_or_error . error ( ) ;
2019-08-17 12:24:50 +00:00
auto guest_inode_id = metadata_or_error . value ( ) . inode ;
return VFS : : the ( ) . unmount ( guest_inode_id ) ;
2019-08-11 13:56:39 +00:00
}
2019-06-07 07:36:51 +00:00
void Process : : FileDescriptionAndFlags : : clear ( )
2019-04-29 02:55:54 +00:00
{
2019-06-13 20:03:04 +00:00
description = nullptr ;
2019-04-29 02:55:54 +00:00
flags = 0 ;
}
2019-07-03 19:17:35 +00:00
void Process : : FileDescriptionAndFlags : : set ( NonnullRefPtr < FileDescription > & & d , u32 f )
2019-04-29 02:55:54 +00:00
{
2019-06-13 20:03:04 +00:00
description = move ( d ) ;
2019-04-29 02:55:54 +00:00
flags = f ;
}
2019-05-03 20:59:58 +00:00
2020-01-11 09:27:37 +00:00
int Process : : sys $ mknod ( const Syscall : : SC_mknod_params * user_params )
2019-05-03 20:59:58 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( dpath ) ;
2020-01-11 09:27:37 +00:00
Syscall : : SC_mknod_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-11 09:27:37 +00:00
if ( ! is_superuser ( ) & & ! is_regular_file ( params . mode ) & & ! is_fifo ( params . mode ) & & ! is_socket ( params . mode ) )
return - EPERM ;
2020-01-11 11:47:47 +00:00
auto path = get_syscall_path_argument ( params . path ) ;
2020-01-11 09:27:37 +00:00
if ( path . is_error ( ) )
return path . error ( ) ;
return VFS : : the ( ) . mknod ( path . value ( ) , params . mode & ~ umask ( ) , params . dev , current_directory ( ) ) ;
2019-05-03 20:59:58 +00:00
}
2019-07-21 07:59:17 +00:00
int Process : : sys $ dump_backtrace ( )
{
dump_backtrace ( ) ;
return 0 ;
}
2019-07-21 17:45:31 +00:00
int Process : : sys $ dbgputch ( u8 ch )
{
IO : : out8 ( 0xe9 , ch ) ;
return 0 ;
}
2019-07-21 19:43:37 +00:00
int Process : : sys $ dbgputstr ( const u8 * characters , int length )
{
2019-08-09 17:44:14 +00:00
if ( ! length )
return 0 ;
2019-07-21 19:43:37 +00:00
if ( ! validate_read ( characters , length ) )
return - EFAULT ;
2020-01-05 17:00:15 +00:00
SmapDisabler disabler ;
2019-07-21 19:43:37 +00:00
for ( int i = 0 ; i < length ; + + i )
IO : : out8 ( 0xe9 , characters [ i ] ) ;
return 0 ;
}
2019-07-25 19:02:19 +00:00
2019-08-07 19:52:43 +00:00
KBuffer Process : : backtrace ( ProcessInspectionHandle & handle ) const
2019-07-25 19:02:19 +00:00
{
2019-08-07 19:52:43 +00:00
KBufferBuilder builder ;
2019-07-25 19:02:19 +00:00
for_each_thread ( [ & ] ( Thread & thread ) {
2019-12-07 19:45:26 +00:00
builder . appendf ( " Thread %d (%s): \n " , thread . tid ( ) , thread . name ( ) . characters ( ) ) ;
2019-07-25 19:02:19 +00:00
builder . append ( thread . backtrace ( handle ) ) ;
return IterationDecision : : Continue ;
} ) ;
2019-08-07 19:52:43 +00:00
return builder . build ( ) ;
2019-07-25 19:02:19 +00:00
}
2019-07-29 05:26:01 +00:00
int Process : : sys $ set_process_icon ( int icon_id )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( shared_buffer ) ;
2019-07-29 05:26:01 +00:00
LOCKER ( shared_buffers ( ) . lock ( ) ) ;
auto it = shared_buffers ( ) . resource ( ) . find ( icon_id ) ;
if ( it = = shared_buffers ( ) . resource ( ) . end ( ) )
return - EINVAL ;
auto & shared_buffer = * ( * it ) . value ;
if ( ! shared_buffer . is_shared_with ( m_pid ) )
return - EPERM ;
m_icon_id = icon_id ;
return 0 ;
}
2019-08-15 18:55:10 +00:00
int Process : : sys $ get_process_name ( char * buffer , int buffer_size )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-08-15 18:55:10 +00:00
if ( buffer_size < = 0 )
return - EINVAL ;
if ( ! validate_write ( buffer , buffer_size ) )
return - EFAULT ;
2020-01-05 17:00:15 +00:00
if ( m_name . length ( ) + 1 > ( size_t ) buffer_size )
2019-08-15 18:55:10 +00:00
return - ENAMETOOLONG ;
2020-01-05 17:00:15 +00:00
copy_to_user ( buffer , m_name . characters ( ) , m_name . length ( ) + 1 ) ;
2019-08-15 18:55:10 +00:00
return 0 ;
}
2019-10-13 14:41:55 +00:00
// We don't use the flag yet, but we could use it for distinguishing
// random source like Linux, unlike the OpenBSD equivalent. However, if we
// do, we should be able of the caveats that Linux has dealt with.
int Process : : sys $ getrandom ( void * buffer , size_t buffer_size , unsigned int flags __attribute__ ( ( unused ) ) )
{
2020-01-12 09:30:02 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-10-13 14:41:55 +00:00
if ( buffer_size < = 0 )
return - EINVAL ;
if ( ! validate_write ( buffer , buffer_size ) )
return - EFAULT ;
2019-10-13 15:55:58 +00:00
2020-01-12 19:10:53 +00:00
SmapDisabler disabler ;
2020-01-03 11:36:30 +00:00
get_good_random_bytes ( ( u8 * ) buffer , buffer_size ) ;
2019-10-13 14:41:55 +00:00
return 0 ;
}
2019-11-02 18:34:06 +00:00
2020-01-13 10:19:18 +00:00
int Process : : sys $ setkeymap ( const Syscall : : SC_setkeymap_params * user_params )
2019-11-23 07:48:07 +00:00
{
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-01-13 10:19:18 +00:00
Syscall : : SC_setkeymap_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
2019-12-31 12:37:38 +00:00
return - EFAULT ;
2020-01-13 10:19:18 +00:00
const char * map = params . map ;
const char * shift_map = params . shift_map ;
const char * alt_map = params . alt_map ;
const char * altgr_map = params . altgr_map ;
2020-01-05 08:13:00 +00:00
if ( ! validate_read ( map , 0x80 ) )
2019-12-31 12:37:38 +00:00
return - EFAULT ;
2020-01-05 08:13:00 +00:00
if ( ! validate_read ( shift_map , 0x80 ) )
2019-11-23 07:48:07 +00:00
return - EFAULT ;
2020-01-05 08:13:00 +00:00
if ( ! validate_read ( alt_map , 0x80 ) )
2019-11-23 07:48:07 +00:00
return - EFAULT ;
2020-01-05 08:13:00 +00:00
if ( ! validate_read ( altgr_map , 0x80 ) )
2019-11-23 07:48:07 +00:00
return - EFAULT ;
2020-01-13 09:26:20 +00:00
SmapDisabler disabler ;
2020-01-05 08:13:00 +00:00
KeyboardDevice : : the ( ) . set_maps ( map , shift_map , alt_map , altgr_map ) ;
2019-11-23 07:48:07 +00:00
return 0 ;
}
2020-02-16 07:13:11 +00:00
int Process : : sys $ clock_gettime ( clockid_t clock_id , timespec * user_ts )
2019-11-02 18:34:06 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-02-16 07:13:11 +00:00
if ( ! validate_write_typed ( user_ts ) )
2019-11-02 18:34:06 +00:00
return - EFAULT ;
2020-02-16 07:13:11 +00:00
timespec ts ;
memset ( & ts , 0 , sizeof ( ts ) ) ;
2019-11-02 18:34:06 +00:00
switch ( clock_id ) {
case CLOCK_MONOTONIC :
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
ts . tv_sec = TimeManagement : : the ( ) . seconds_since_boot ( ) ;
ts . tv_nsec = TimeManagement : : the ( ) . ticks_this_second ( ) * 1000000 ;
break ;
case CLOCK_REALTIME :
ts . tv_sec = TimeManagement : : the ( ) . epoch_time ( ) ;
ts . tv_nsec = TimeManagement : : the ( ) . ticks_this_second ( ) * 1000000 ;
2019-11-02 18:34:06 +00:00
break ;
default :
return - EINVAL ;
}
2020-02-16 07:13:11 +00:00
copy_to_user ( user_ts , & ts ) ;
2019-11-02 18:34:06 +00:00
return 0 ;
}
2020-03-12 23:17:14 +00:00
int Process : : sys $ clock_settime ( clockid_t clock_id , timespec * user_ts )
{
SmapDisabler disabler ;
REQUIRE_PROMISE ( stdio ) ;
if ( ! validate_write_typed ( user_ts ) )
return - EFAULT ;
switch ( clock_id ) {
case CLOCK_REALTIME :
TimeManagement : : the ( ) . set_epoch_time ( user_ts - > tv_sec ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2020-01-11 10:51:03 +00:00
int Process : : sys $ clock_nanosleep ( const Syscall : : SC_clock_nanosleep_params * user_params )
2019-11-02 18:34:06 +00:00
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-01-13 10:19:18 +00:00
2020-01-11 10:51:03 +00:00
Syscall : : SC_clock_nanosleep_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2019-11-02 18:34:06 +00:00
2020-01-11 10:51:03 +00:00
if ( params . requested_sleep & & ! validate_read_typed ( params . requested_sleep ) )
2019-11-02 18:34:06 +00:00
return - EFAULT ;
2020-01-11 10:51:03 +00:00
timespec requested_sleep ;
copy_from_user ( & requested_sleep , params . requested_sleep ) ;
if ( params . remaining_sleep & & ! validate_write_typed ( params . remaining_sleep ) )
2019-11-02 18:34:06 +00:00
return - EFAULT ;
2020-01-11 10:51:03 +00:00
bool is_absolute = params . flags & TIMER_ABSTIME ;
2019-11-02 18:34:06 +00:00
2020-01-11 10:51:03 +00:00
switch ( params . clock_id ) {
2019-11-02 18:34:06 +00:00
case CLOCK_MONOTONIC : {
u64 wakeup_time ;
if ( is_absolute ) {
2020-01-11 10:51:03 +00:00
u64 time_to_wake = ( requested_sleep . tv_sec * 1000 + requested_sleep . tv_nsec / 1000000 ) ;
2020-02-17 14:04:27 +00:00
wakeup_time = Thread : : current - > sleep_until ( time_to_wake ) ;
2019-11-02 18:34:06 +00:00
} else {
2020-01-11 10:51:03 +00:00
u32 ticks_to_sleep = ( requested_sleep . tv_sec * 1000 + requested_sleep . tv_nsec / 1000000 ) ;
2019-11-02 18:34:06 +00:00
if ( ! ticks_to_sleep )
return 0 ;
2020-02-17 14:04:27 +00:00
wakeup_time = Thread : : current - > sleep ( ticks_to_sleep ) ;
2019-11-02 18:34:06 +00:00
}
if ( wakeup_time > g_uptime ) {
u32 ticks_left = wakeup_time - g_uptime ;
2020-01-11 10:51:03 +00:00
if ( ! is_absolute & & params . remaining_sleep ) {
2020-03-03 04:12:39 +00:00
if ( ! validate_write_typed ( params . remaining_sleep ) ) {
// This can happen because the lock is dropped while
// sleeping, thus giving other threads the opportunity
// to make the region unwritable.
return - EFAULT ;
}
2020-01-11 10:51:03 +00:00
timespec remaining_sleep ;
memset ( & remaining_sleep , 0 , sizeof ( timespec ) ) ;
Kernel: Introduce the new Time management subsystem
This new subsystem includes better abstractions of how time will be
handled in the OS. We take advantage of the existing RTC timer to aid
in keeping time synchronized. This is standing in contrast to how we
handled time-keeping in the kernel, where the PIT was responsible for
that function in addition to update the scheduler about ticks.
With that new advantage, we can easily change the ticking dynamically
and still keep the time synchronized.
In the process context, we no longer use a fixed declaration of
TICKS_PER_SECOND, but we call the TimeManagement singleton class to
provide us the right value. This allows us to use dynamic ticking in
the future, a feature known as tickless kernel.
The scheduler no longer does by himself the calculation of real time
(Unix time), and just calls the TimeManagment singleton class to provide
the value.
Also, we can use 2 new boot arguments:
- the "time" boot argument accpets either the value "modern", or
"legacy". If "modern" is specified, the time management subsystem will
try to setup HPET. Otherwise, for "legacy" value, the time subsystem
will revert to use the PIT & RTC, leaving HPET disabled.
If this boot argument is not specified, the default pattern is to try
to setup HPET.
- the "hpet" boot argumet accepts either the value "periodic" or
"nonperiodic". If "periodic" is specified, the HPET will scan for
periodic timers, and will assert if none are found. If only one is
found, that timer will be assigned for the time-keeping task. If more
than one is found, both time-keeping task & scheduler-ticking task
will be assigned to periodic timers.
If this boot argument is not specified, the default pattern is to try
to scan for HPET periodic timers. This boot argument has no effect if
HPET is disabled.
In hardware context, PIT & RealTimeClock classes are merely inheriting
from the HardwareTimer class, and they allow to use the old i8254 (PIT)
and RTC devices, managing them via IO ports. By default, the RTC will be
programmed to a frequency of 1024Hz. The PIT will be programmed to a
frequency close to 1000Hz.
About HPET, depending if we need to scan for periodic timers or not,
we try to set a frequency close to 1000Hz for the time-keeping timer
and scheduler-ticking timer. Also, if possible, we try to enable the
Legacy replacement feature of the HPET. This feature if exists,
instructs the chipset to disconnect both i8254 (PIT) and RTC.
This behavior is observable on QEMU, and was verified against the source
code:
https://github.com/qemu/qemu/commit/ce967e2f33861b0e17753f97fa4527b5943c94b6
The HPETComparator class is inheriting from HardwareTimer class, and is
responsible for an individual HPET comparator, which is essentially a
timer. Therefore, it needs to call the singleton HPET class to perform
HPET-related operations.
The new abstraction of Hardware timers brings an opportunity of more new
features in the foreseeable future. For example, we can change the
callback function of each hardware timer, thus it makes it possible to
swap missions between hardware timers, or to allow to use a hardware
timer for other temporary missions (e.g. calibrating the LAPIC timer,
measuring the CPU frequency, etc).
2020-03-09 15:03:27 +00:00
remaining_sleep . tv_sec = ticks_left / TimeManagement : : the ( ) . ticks_per_second ( ) ;
ticks_left - = remaining_sleep . tv_sec * TimeManagement : : the ( ) . ticks_per_second ( ) ;
2020-01-11 10:51:03 +00:00
remaining_sleep . tv_nsec = ticks_left * 1000000 ;
copy_to_user ( params . remaining_sleep , & remaining_sleep ) ;
2019-11-02 18:34:06 +00:00
}
return - EINTR ;
}
return 0 ;
}
default :
return - EINVAL ;
}
}
2019-11-09 21:18:16 +00:00
int Process : : sys $ sync ( )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-11-09 21:18:16 +00:00
VFS : : the ( ) . sync ( ) ;
return 0 ;
}
int Process : : sys $ yield ( )
{
2020-01-12 09:30:02 +00:00
REQUIRE_PROMISE ( stdio ) ;
2020-02-17 14:04:27 +00:00
Thread : : current - > yield_without_holding_big_lock ( ) ;
2019-11-16 11:18:59 +00:00
return 0 ;
2019-11-09 21:18:16 +00:00
}
int Process : : sys $ beep ( )
{
2019-12-26 21:31:26 +00:00
PCSpeaker : : tone_on ( 440 ) ;
2020-02-17 14:04:27 +00:00
u64 wakeup_time = Thread : : current - > sleep ( 100 ) ;
2019-12-26 21:31:26 +00:00
PCSpeaker : : tone_off ( ) ;
if ( wakeup_time > g_uptime )
return - EINTR ;
2019-11-09 21:18:16 +00:00
return 0 ;
}
2019-11-28 19:59:11 +00:00
2020-01-06 10:36:16 +00:00
int Process : : sys $ module_load ( const char * user_path , size_t path_length )
2019-11-28 19:59:11 +00:00
{
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-06 10:36:16 +00:00
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-01-06 10:36:16 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
2020-01-18 22:04:48 +00:00
auto description_or_error = VFS : : the ( ) . open ( path . value ( ) , O_RDONLY , 0 , current_directory ( ) ) ;
2019-11-28 19:59:11 +00:00
if ( description_or_error . is_error ( ) )
return description_or_error . error ( ) ;
auto & description = description_or_error . value ( ) ;
auto payload = description - > read_entire_file ( ) ;
auto storage = KBuffer : : create_with_size ( payload . size ( ) ) ;
memcpy ( storage . data ( ) , payload . data ( ) , payload . size ( ) ) ;
payload . clear ( ) ;
2020-04-11 18:24:07 +00:00
auto elf_image = make < ELF : : Image > ( storage . data ( ) , storage . size ( ) ) ;
2019-11-28 19:59:11 +00:00
if ( ! elf_image - > parse ( ) )
return - ENOEXEC ;
HashMap < String , u8 * > section_storage_by_name ;
auto module = make < Module > ( ) ;
2020-04-11 18:24:07 +00:00
elf_image - > for_each_section_of_type ( SHT_PROGBITS , [ & ] ( const ELF : : Image : : Section & section ) {
2020-04-10 14:35:40 +00:00
if ( ! section . size ( ) )
return IterationDecision : : Continue ;
2020-01-03 02:44:55 +00:00
auto section_storage = KBuffer : : copy ( section . raw_data ( ) , section . size ( ) , Region : : Access : : Read | Region : : Access : : Write | Region : : Access : : Execute ) ;
2019-11-28 19:59:11 +00:00
section_storage_by_name . set ( section . name ( ) , section_storage . data ( ) ) ;
module - > sections . append ( move ( section_storage ) ) ;
return IterationDecision : : Continue ;
} ) ;
2019-12-24 03:09:18 +00:00
bool missing_symbols = false ;
2020-04-11 18:24:07 +00:00
elf_image - > for_each_section_of_type ( SHT_PROGBITS , [ & ] ( const ELF : : Image : : Section & section ) {
2020-04-10 14:35:40 +00:00
if ( ! section . size ( ) )
return IterationDecision : : Continue ;
2019-11-28 19:59:11 +00:00
auto * section_storage = section_storage_by_name . get ( section . name ( ) ) . value_or ( nullptr ) ;
ASSERT ( section_storage ) ;
2020-04-11 18:24:07 +00:00
section . relocations ( ) . for_each_relocation ( [ & ] ( const ELF : : Image : : Relocation & relocation ) {
2019-11-28 19:59:11 +00:00
auto & patch_ptr = * reinterpret_cast < ptrdiff_t * > ( section_storage + relocation . offset ( ) ) ;
switch ( relocation . type ( ) ) {
case R_386_PC32 : {
// PC-relative relocation
dbg ( ) < < " PC-relative relocation: " < < relocation . symbol ( ) . name ( ) ;
2019-11-28 20:30:20 +00:00
u32 symbol_address = address_for_kernel_symbol ( relocation . symbol ( ) . name ( ) ) ;
2019-12-24 03:09:18 +00:00
if ( symbol_address = = 0 )
missing_symbols = true ;
2019-11-28 19:59:11 +00:00
dbg ( ) < < " Symbol address: " < < ( void * ) symbol_address ;
ptrdiff_t relative_offset = ( char * ) symbol_address - ( ( char * ) & patch_ptr + 4 ) ;
patch_ptr = relative_offset ;
break ;
}
case R_386_32 : // Absolute relocation
dbg ( ) < < " Absolute relocation: ' " < < relocation . symbol ( ) . name ( ) < < " ' value: " < < relocation . symbol ( ) . value ( ) < < " , index: " < < relocation . symbol_index ( ) ;
2019-11-28 20:30:20 +00:00
if ( relocation . symbol ( ) . bind ( ) = = STB_LOCAL ) {
auto * section_storage_containing_symbol = section_storage_by_name . get ( relocation . symbol ( ) . section ( ) . name ( ) ) . value_or ( nullptr ) ;
ASSERT ( section_storage_containing_symbol ) ;
2019-12-24 03:09:18 +00:00
u32 symbol_address = ( ptrdiff_t ) ( section_storage_containing_symbol + relocation . symbol ( ) . value ( ) ) ;
if ( symbol_address = = 0 )
missing_symbols = true ;
dbg ( ) < < " Symbol address: " < < ( void * ) symbol_address ;
patch_ptr + = symbol_address ;
2019-11-28 20:30:20 +00:00
} else if ( relocation . symbol ( ) . bind ( ) = = STB_GLOBAL ) {
2019-12-24 03:09:18 +00:00
u32 symbol_address = address_for_kernel_symbol ( relocation . symbol ( ) . name ( ) ) ;
if ( symbol_address = = 0 )
missing_symbols = true ;
dbg ( ) < < " Symbol address: " < < ( void * ) symbol_address ;
patch_ptr + = symbol_address ;
2019-11-28 20:30:20 +00:00
} else {
ASSERT_NOT_REACHED ( ) ;
}
2019-11-28 19:59:11 +00:00
break ;
}
return IterationDecision : : Continue ;
} ) ;
return IterationDecision : : Continue ;
} ) ;
2019-12-24 03:09:18 +00:00
if ( missing_symbols )
2020-01-18 22:22:40 +00:00
return - EINVAL ;
2019-12-24 03:09:18 +00:00
2019-11-28 19:59:11 +00:00
auto * text_base = section_storage_by_name . get ( " .text " ) . value_or ( nullptr ) ;
if ( ! text_base ) {
dbg ( ) < < " No .text section found in module! " ;
return - EINVAL ;
}
2020-04-11 18:24:07 +00:00
elf_image - > for_each_symbol ( [ & ] ( const ELF : : Image : : Symbol & symbol ) {
2019-11-28 19:59:11 +00:00
dbg ( ) < < " - " < < symbol . type ( ) < < " ' " < < symbol . name ( ) < < " ' @ " < < ( void * ) symbol . value ( ) < < " , size= " < < symbol . size ( ) ;
2020-01-16 21:04:44 +00:00
if ( symbol . name ( ) = = " module_init " ) {
2019-11-28 20:07:22 +00:00
module - > module_init = ( ModuleInitPtr ) ( text_base + symbol . value ( ) ) ;
2020-01-16 21:04:44 +00:00
} else if ( symbol . name ( ) = = " module_fini " ) {
2019-11-28 20:07:22 +00:00
module - > module_fini = ( ModuleFiniPtr ) ( text_base + symbol . value ( ) ) ;
2020-01-16 21:04:44 +00:00
} else if ( symbol . name ( ) = = " module_name " ) {
2019-11-29 20:31:17 +00:00
const u8 * storage = section_storage_by_name . get ( symbol . section ( ) . name ( ) ) . value_or ( nullptr ) ;
if ( storage )
module - > name = String ( ( const char * ) ( storage + symbol . value ( ) ) ) ;
2019-11-28 19:59:11 +00:00
}
return IterationDecision : : Continue ;
} ) ;
2019-11-28 20:07:22 +00:00
if ( ! module - > module_init )
2019-11-28 19:59:11 +00:00
return - EINVAL ;
2019-12-24 00:40:26 +00:00
if ( g_modules - > contains ( module - > name ) ) {
dbg ( ) < < " a module with the name " < < module - > name < < " is already loaded; please unload it first " ;
return - EEXIST ;
}
2019-11-28 20:07:22 +00:00
module - > module_init ( ) ;
2019-11-28 19:59:11 +00:00
auto name = module - > name ;
g_modules - > set ( name , move ( module ) ) ;
return 0 ;
}
2020-01-11 11:47:47 +00:00
int Process : : sys $ module_unload ( const char * user_name , size_t name_length )
2019-11-28 19:59:11 +00:00
{
if ( ! is_superuser ( ) )
return - EPERM ;
2019-11-28 20:07:22 +00:00
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2020-01-11 11:47:47 +00:00
auto module_name = validate_and_copy_string_from_user ( user_name , name_length ) ;
if ( module_name . is_null ( ) )
return - EFAULT ;
2020-01-06 10:36:16 +00:00
auto it = g_modules - > find ( module_name ) ;
2019-11-28 20:07:22 +00:00
if ( it = = g_modules - > end ( ) )
return - ENOENT ;
if ( it - > value - > module_fini )
it - > value - > module_fini ( ) ;
g_modules - > remove ( it ) ;
2019-11-28 19:59:11 +00:00
return 0 ;
}
2019-12-11 19:36:56 +00:00
int Process : : sys $ profiling_enable ( pid_t pid )
{
2020-01-11 19:48:43 +00:00
REQUIRE_NO_PROMISES ;
2019-12-11 19:36:56 +00:00
InterruptDisabler disabler ;
auto * process = Process : : from_pid ( pid ) ;
if ( ! process )
return - ESRCH ;
2020-02-27 09:42:31 +00:00
if ( process - > is_dead ( ) )
return - ESRCH ;
2019-12-11 19:36:56 +00:00
if ( ! is_superuser ( ) & & process - > uid ( ) ! = m_uid )
return - EPERM ;
Profiling : : start ( * process ) ;
process - > set_profiling ( true ) ;
return 0 ;
}
int Process : : sys $ profiling_disable ( pid_t pid )
{
InterruptDisabler disabler ;
auto * process = Process : : from_pid ( pid ) ;
if ( ! process )
return - ESRCH ;
if ( ! is_superuser ( ) & & process - > uid ( ) ! = m_uid )
return - EPERM ;
process - > set_profiling ( false ) ;
Profiling : : stop ( ) ;
return 0 ;
}
2019-12-15 20:29:26 +00:00
void * Process : : sys $ get_kernel_info_page ( )
{
2020-01-12 09:30:02 +00:00
REQUIRE_PROMISE ( stdio ) ;
2019-12-15 21:21:28 +00:00
return s_info_page_address_for_userspace . as_ptr ( ) ;
2019-12-15 20:29:26 +00:00
}
2019-12-22 10:35:02 +00:00
Thread & Process : : any_thread ( )
{
Thread * found_thread = nullptr ;
for_each_thread ( [ & ] ( auto & thread ) {
found_thread = & thread ;
return IterationDecision : : Break ;
} ) ;
ASSERT ( found_thread ) ;
return * found_thread ;
}
2019-12-22 20:29:47 +00:00
WaitQueue & Process : : futex_queue ( i32 * userspace_address )
{
2020-03-08 09:36:51 +00:00
auto & queue = m_futex_queues . ensure ( ( FlatPtr ) userspace_address ) ;
2019-12-22 20:29:47 +00:00
if ( ! queue )
queue = make < WaitQueue > ( ) ;
return * queue ;
}
2020-01-06 10:44:15 +00:00
int Process : : sys $ futex ( const Syscall : : SC_futex_params * user_params )
2019-12-22 20:29:47 +00:00
{
2020-01-12 09:27:42 +00:00
REQUIRE_PROMISE ( thread ) ;
2019-12-22 20:29:47 +00:00
2020-01-06 10:44:15 +00:00
Syscall : : SC_futex_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-05 17:00:15 +00:00
2020-01-06 10:44:15 +00:00
i32 * userspace_address = params . userspace_address ;
int futex_op = params . futex_op ;
i32 value = params . val ;
const timespec * user_timeout = params . timeout ;
2019-12-22 20:29:47 +00:00
if ( ! validate_read_typed ( userspace_address ) )
return - EFAULT ;
2020-01-06 10:44:15 +00:00
if ( user_timeout & & ! validate_read_typed ( user_timeout ) )
2019-12-22 20:29:47 +00:00
return - EFAULT ;
2020-01-06 10:44:15 +00:00
timespec timeout { 0 , 0 } ;
if ( user_timeout )
2020-01-13 07:00:35 +00:00
copy_from_user ( & timeout , user_timeout ) ;
2020-01-06 10:44:15 +00:00
i32 user_value ;
2019-12-22 20:29:47 +00:00
switch ( futex_op ) {
case FUTEX_WAIT :
2020-01-13 07:00:35 +00:00
copy_from_user ( & user_value , userspace_address ) ;
2020-01-06 10:44:15 +00:00
if ( user_value ! = value )
2019-12-22 20:29:47 +00:00
return - EAGAIN ;
// FIXME: This is supposed to be interruptible by a signal, but right now WaitQueue cannot be interrupted.
// FIXME: Support timeout!
2020-02-17 14:04:27 +00:00
Thread : : current - > wait_on ( futex_queue ( userspace_address ) ) ;
2019-12-22 20:29:47 +00:00
break ;
case FUTEX_WAKE :
if ( value = = 0 )
return 0 ;
if ( value = = 1 ) {
futex_queue ( userspace_address ) . wake_one ( ) ;
} else {
// FIXME: Wake exactly (value) waiters.
futex_queue ( userspace_address ) . wake_all ( ) ;
}
break ;
}
return 0 ;
}
2019-12-30 18:23:13 +00:00
int Process : : sys $ set_thread_boost ( int tid , int amount )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2019-12-30 18:23:13 +00:00
if ( amount < 0 | | amount > 20 )
return - EINVAL ;
InterruptDisabler disabler ;
auto * thread = Thread : : from_tid ( tid ) ;
if ( ! thread )
return - ESRCH ;
if ( thread - > state ( ) = = Thread : : State : : Dead | | thread - > state ( ) = = Thread : : State : : Dying )
return - ESRCH ;
if ( ! is_superuser ( ) & & thread - > process ( ) . uid ( ) ! = euid ( ) )
return - EPERM ;
thread - > set_priority_boost ( amount ) ;
return 0 ;
}
2019-12-30 19:10:00 +00:00
int Process : : sys $ set_process_boost ( pid_t pid , int amount )
{
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( proc ) ;
2019-12-30 19:10:00 +00:00
if ( amount < 0 | | amount > 20 )
return - EINVAL ;
InterruptDisabler disabler ;
auto * process = Process : : from_pid ( pid ) ;
if ( ! process | | process - > is_dead ( ) )
return - ESRCH ;
if ( ! is_superuser ( ) & & process - > uid ( ) ! = euid ( ) )
return - EPERM ;
process - > m_priority_boost = amount ;
return 0 ;
}
2020-01-10 22:14:04 +00:00
2020-01-12 18:42:01 +00:00
int Process : : sys $ chroot ( const char * user_path , size_t path_length , int mount_flags )
2020-01-10 22:14:04 +00:00
{
if ( ! is_superuser ( ) )
return - EPERM ;
2020-01-11 19:48:43 +00:00
REQUIRE_PROMISE ( chroot ) ;
2020-01-10 22:14:04 +00:00
auto path = get_syscall_path_argument ( user_path , path_length ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
auto directory_or_error = VFS : : the ( ) . open_directory ( path . value ( ) , current_directory ( ) ) ;
if ( directory_or_error . is_error ( ) )
return directory_or_error . error ( ) ;
2020-01-11 15:25:26 +00:00
auto directory = directory_or_error . value ( ) ;
2020-01-12 18:03:42 +00:00
m_root_directory_relative_to_global_root = directory ;
int chroot_mount_flags = mount_flags = = - 1 ? directory - > mount_flags ( ) : mount_flags ;
set_root_directory ( Custody : : create ( nullptr , " " , directory - > inode ( ) , chroot_mount_flags ) ) ;
2020-01-10 22:14:04 +00:00
return 0 ;
}
Custody & Process : : root_directory ( )
{
if ( ! m_root_directory )
m_root_directory = VFS : : the ( ) . root_custody ( ) ;
return * m_root_directory ;
}
2020-01-12 18:42:01 +00:00
Custody & Process : : root_directory_relative_to_global_root ( )
2020-01-10 22:48:44 +00:00
{
2020-01-12 18:42:01 +00:00
if ( ! m_root_directory_relative_to_global_root )
m_root_directory_relative_to_global_root = root_directory ( ) ;
return * m_root_directory_relative_to_global_root ;
2020-01-10 22:48:44 +00:00
}
2020-01-10 22:14:04 +00:00
void Process : : set_root_directory ( const Custody & root )
{
m_root_directory = root ;
}
2020-01-11 19:48:43 +00:00
int Process : : sys $ pledge ( const Syscall : : SC_pledge_params * user_params )
{
Syscall : : SC_pledge_params params ;
2020-01-13 10:19:18 +00:00
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-01-11 19:48:43 +00:00
if ( params . promises . length > 1024 | | params . execpromises . length > 1024 )
return - E2BIG ;
String promises ;
if ( params . promises . characters ) {
promises = validate_and_copy_string_from_user ( params . promises ) ;
if ( promises . is_null ( ) )
return - EFAULT ;
}
String execpromises ;
if ( params . execpromises . characters ) {
execpromises = validate_and_copy_string_from_user ( params . execpromises ) ;
if ( execpromises . is_null ( ) )
return - EFAULT ;
}
auto parse_pledge = [ & ] ( auto & pledge_spec , u32 & mask ) {
auto parts = pledge_spec . split_view ( ' ' ) ;
for ( auto & part : parts ) {
# define __ENUMERATE_PLEDGE_PROMISE(x) \
if ( part = = # x ) { \
mask | = ( 1u < < ( u32 ) Pledge : : x ) ; \
continue ; \
}
ENUMERATE_PLEDGE_PROMISES
# undef __ENUMERATE_PLEDGE_PROMISE
if ( part = = " dns " ) {
// "dns" is an alias for "unix" since DNS queries go via LookupServer
mask | = ( 1u < < ( u32 ) Pledge : : unix ) ;
continue ;
}
return false ;
}
return true ;
} ;
if ( ! promises . is_null ( ) ) {
u32 new_promises = 0 ;
if ( ! parse_pledge ( promises , new_promises ) )
return - EINVAL ;
2020-01-12 11:14:08 +00:00
if ( m_promises & & ( ! new_promises | | new_promises & ~ m_promises ) )
2020-01-11 19:48:43 +00:00
return - EPERM ;
m_promises = new_promises ;
}
if ( ! execpromises . is_null ( ) ) {
u32 new_execpromises = 0 ;
if ( ! parse_pledge ( execpromises , new_execpromises ) )
return - EINVAL ;
2020-01-12 11:14:08 +00:00
if ( m_execpromises & & ( ! new_execpromises | | new_execpromises & ~ m_execpromises ) )
2020-01-11 19:48:43 +00:00
return - EPERM ;
m_execpromises = new_execpromises ;
}
return 0 ;
}
2020-01-19 15:25:38 +00:00
Region & Process : : add_region ( NonnullOwnPtr < Region > region )
{
auto * ptr = region . ptr ( ) ;
m_regions . append ( move ( region ) ) ;
return * ptr ;
}
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
int Process : : sys $ unveil ( const Syscall : : SC_unveil_params * user_params )
{
Syscall : : SC_unveil_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
if ( ! params . path . characters & & ! params . permissions . characters ) {
2020-01-21 18:28:29 +00:00
m_veil_state = VeilState : : Locked ;
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
return 0 ;
}
2020-01-21 18:28:29 +00:00
if ( m_veil_state = = VeilState : : Locked )
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
return - EPERM ;
if ( ! params . path . characters | | ! params . permissions . characters )
return - EINVAL ;
if ( params . permissions . length > 4 )
return - EINVAL ;
auto path = get_syscall_path_argument ( params . path ) ;
if ( path . is_error ( ) )
return path . error ( ) ;
if ( path . value ( ) . is_empty ( ) | | path . value ( ) . characters ( ) [ 0 ] ! = ' / ' )
return - EINVAL ;
2020-03-19 08:57:34 +00:00
auto custody_or_error = VFS : : the ( ) . resolve_path_without_veil ( path . value ( ) , root_directory ( ) ) ;
if ( custody_or_error . is_error ( ) )
// FIXME Should this be EINVAL?
return custody_or_error . error ( ) ;
auto & custody = custody_or_error . value ( ) ;
auto new_unveiled_path = custody - > absolute_path ( ) ;
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
auto permissions = validate_and_copy_string_from_user ( params . permissions ) ;
if ( permissions . is_null ( ) )
return - EFAULT ;
unsigned new_permissions = 0 ;
for ( size_t i = 0 ; i < permissions . length ( ) ; + + i ) {
switch ( permissions [ i ] ) {
case ' r ' :
new_permissions | = UnveiledPath : : Access : : Read ;
break ;
case ' w ' :
new_permissions | = UnveiledPath : : Access : : Write ;
break ;
case ' x ' :
new_permissions | = UnveiledPath : : Access : : Execute ;
break ;
case ' c ' :
new_permissions | = UnveiledPath : : Access : : CreateOrRemove ;
break ;
default :
return - EINVAL ;
}
}
2020-02-25 13:49:47 +00:00
for ( size_t i = 0 ; i < m_unveiled_paths . size ( ) ; + + i ) {
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
auto & unveiled_path = m_unveiled_paths [ i ] ;
2020-03-19 08:57:34 +00:00
if ( unveiled_path . path = = new_unveiled_path ) {
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
if ( new_permissions & ~ unveiled_path . permissions )
return - EPERM ;
unveiled_path . permissions = new_permissions ;
return 0 ;
}
}
2020-03-19 08:57:34 +00:00
m_unveiled_paths . append ( { new_unveiled_path , new_permissions } ) ;
2020-01-21 18:28:29 +00:00
ASSERT ( m_veil_state ! = VeilState : : Locked ) ;
m_veil_state = VeilState : : Dropped ;
Kernel: Add a basic implementation of unveil()
This syscall is a complement to pledge() and adds the same sort of
incremental relinquishing of capabilities for filesystem access.
The first call to unveil() will "drop a veil" on the process, and from
now on, only unveiled parts of the filesystem are visible to it.
Each call to unveil() specifies a path to either a directory or a file
along with permissions for that path. The permissions are a combination
of the following:
- r: Read access (like the "rpath" promise)
- w: Write access (like the "wpath" promise)
- x: Execute access
- c: Create/remove access (like the "cpath" promise)
Attempts to open a path that has not been unveiled with fail with
ENOENT. If the unveiled path lacks sufficient permissions, it will fail
with EACCES.
Like pledge(), subsequent calls to unveil() with the same path can only
remove permissions, not add them.
Once you call unveil(nullptr, nullptr), the veil is locked, and it's no
longer possible to unveil any more paths for the process, ever.
This concept comes from OpenBSD, and their implementation does various
things differently, I'm sure. This is just a first implementation for
SerenityOS, and we'll keep improving on it as we go. :^)
2020-01-20 21:12:04 +00:00
return 0 ;
}
2020-02-02 19:26:27 +00:00
2020-03-08 09:36:51 +00:00
int Process : : sys $ perf_event ( int type , FlatPtr arg1 , FlatPtr arg2 )
2020-02-02 19:26:27 +00:00
{
if ( ! m_perf_event_buffer )
m_perf_event_buffer = make < PerformanceEventBuffer > ( ) ;
return m_perf_event_buffer - > append ( type , arg1 , arg2 ) ;
}
2020-02-16 00:27:42 +00:00
2020-02-16 01:01:42 +00:00
void Process : : set_tty ( TTY * tty )
{
m_tty = tty ;
}
2020-03-02 09:40:40 +00:00
OwnPtr < Process : : ELFBundle > Process : : elf_bundle ( ) const
{
if ( ! m_executable )
return nullptr ;
auto bundle = make < ELFBundle > ( ) ;
2020-04-07 15:23:37 +00:00
if ( ! m_executable - > inode ( ) . shared_vmobject ( ) ) {
return nullptr ;
}
2020-03-02 09:40:40 +00:00
ASSERT ( m_executable - > inode ( ) . shared_vmobject ( ) ) ;
auto & vmobject = * m_executable - > inode ( ) . shared_vmobject ( ) ;
bundle - > region = MM . allocate_kernel_region_with_vmobject ( const_cast < SharedInodeVMObject & > ( vmobject ) , vmobject . size ( ) , " ELF bundle " , Region : : Access : : Read ) ;
if ( ! bundle - > region )
return nullptr ;
2020-04-11 18:24:07 +00:00
bundle - > elf_loader = make < ELF : : Loader > ( bundle - > region - > vaddr ( ) . as_ptr ( ) , bundle - > region - > size ( ) ) ;
2020-03-02 09:40:40 +00:00
return bundle ;
}
2020-03-16 18:06:33 +00:00
int Process : : sys $ get_stack_bounds ( FlatPtr * user_stack_base , size_t * user_stack_size )
{
if ( ! validate_write_typed ( user_stack_base ) )
return - EFAULT ;
if ( ! validate_write_typed ( user_stack_size ) )
return - EFAULT ;
FlatPtr stack_pointer = Thread : : current - > get_register_dump_from_stack ( ) . userspace_esp ;
auto * stack_region = MM . region_from_vaddr ( * this , VirtualAddress ( stack_pointer ) ) ;
if ( ! stack_region ) {
ASSERT_NOT_REACHED ( ) ;
return - EINVAL ;
}
FlatPtr stack_base = stack_region - > range ( ) . base ( ) . get ( ) ;
size_t stack_size = stack_region - > size ( ) ;
copy_to_user ( user_stack_base , & stack_base ) ;
copy_to_user ( user_stack_size , & stack_size ) ;
return 0 ;
}
2020-03-28 08:47:16 +00:00
int Process : : sys $ ptrace ( const Syscall : : SC_ptrace_params * user_params )
{
REQUIRE_PROMISE ( proc ) ;
Syscall : : SC_ptrace_params params ;
if ( ! validate_read_and_copy_typed ( & params , user_params ) )
return - EFAULT ;
2020-04-07 15:23:37 +00:00
auto result = Ptrace : : handle_syscall ( params , * this ) ;
return result . is_error ( ) ? result . error ( ) : result . value ( ) ;
2020-03-28 08:47:16 +00:00
}
bool Process : : has_tracee_thread ( int tracer_pid ) const
{
bool has_tracee = false ;
for_each_thread ( [ & ] ( Thread & t ) {
if ( t . tracer ( ) & & t . tracer ( ) - > tracer_pid ( ) = = tracer_pid ) {
has_tracee = true ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
return has_tracee ;
}
2020-04-07 15:23:37 +00:00
KResultOr < u32 > Process : : peek_user_data ( u32 * address )
{
if ( ! MM . validate_user_read ( * this , VirtualAddress ( address ) , sizeof ( u32 ) ) ) {
2020-04-10 14:35:49 +00:00
dbg ( ) < < " Invalid address for peek_user_data: " < < address ;
2020-04-07 15:23:37 +00:00
return KResult ( - EFAULT ) ;
}
uint32_t result ;
SmapDisabler dis ;
// This function can be called from the context of another
// process that called PT_PEEK
ProcessPagingScope scope ( * this ) ;
result = * address ;
return result ;
}
KResult Process : : poke_user_data ( u32 * address , u32 data )
{
// We validate for read (rather than write) because PT_POKE can write to readonly pages.
2020-04-10 14:35:49 +00:00
// So we effectively only care that the poke operation is trying to write to user pages.
2020-04-07 15:23:37 +00:00
if ( ! MM . validate_user_read ( * this , VirtualAddress ( address ) , sizeof ( u32 ) ) ) {
2020-04-10 14:35:49 +00:00
dbg ( ) < < " Invalid address for poke_user_data: " < < address ;
2020-04-07 15:23:37 +00:00
return KResult ( - EFAULT ) ;
}
ProcessPagingScope scope ( * this ) ;
Range range = { VirtualAddress ( address ) , sizeof ( u32 ) } ;
auto * region = region_containing ( range ) ;
ASSERT ( region ! = nullptr ) ;
if ( region - > is_shared ( ) ) {
// If the region is shared, we change its vmobject to a PrivateInodeVMObject
// to prevent the write operation from chaning any shared inode data
ASSERT ( region - > vmobject ( ) . is_shared_inode ( ) ) ;
region - > set_vmobject ( PrivateInodeVMObject : : create_with_inode ( static_cast < SharedInodeVMObject & > ( region - > vmobject ( ) ) . inode ( ) ) ) ;
region - > set_shared ( false ) ;
}
const bool was_writable = region - > is_writable ( ) ;
if ( ! was_writable ) //TODO refactor into scopeguard
{
region - > set_writable ( true ) ;
region - > remap ( ) ;
}
{
SmapDisabler dis ;
* address = data ;
}
if ( ! was_writable ) {
region - > set_writable ( false ) ;
region - > remap ( ) ;
}
return KResult ( KSuccess ) ;
}
2020-02-16 00:27:42 +00:00
}