Serenity Operating System
at master 188 lines 6.5 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, Alex Major 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/Assertions.h> 9#include <AK/Debug.h> 10#include <AK/Iterator.h> 11#include <AK/Vector.h> 12#include <Kernel/API/SyscallString.h> 13#include <LibCore/ArgsParser.h> 14#include <LibMain/Main.h> 15#include <errno_codes.h> 16#include <stdio.h> 17#include <stdlib.h> 18#include <string.h> 19#include <sys/mman.h> 20#include <syscall.h> 21 22#define SC_NARG 4 23 24FlatPtr arg[SC_NARG]; 25char outbuf[BUFSIZ]; 26 27using Arguments = Vector<DeprecatedString>; 28using ArgIter = Arguments::Iterator; 29 30static FlatPtr parse_from(ArgIter&); 31 32template<> 33struct AK::Formatter<Syscall::Function> : Formatter<StringView> { 34 ErrorOr<void> format(FormatBuilder& builder, Syscall::Function function) 35 { 36 return Formatter<StringView>::format(builder, to_string(function)); 37 } 38}; 39 40ErrorOr<int> serenity_main(Main::Arguments arguments) 41{ 42 bool output_buffer = false; 43 bool list_syscalls = false; 44 Arguments syscall_arguments; 45 46 Core::ArgsParser args_parser; 47 args_parser.set_general_help( 48 "Enables you to do a direct syscall, even those that use a 'SC_*_params' buffer.\n" 49 "Arguments can be literal strings, numbers, the output buffer, or parameter buffers:\n" 50 " - Arguments that begin with a comma are stripped of the comma and treated as string arguments, for example ',0x0' or ',['.\n" 51 " - 'buf' is replaced by a pointer to the output buffer.\n" 52 " - Numbers can be written like 1234 or 0xDEADC0DE.\n" 53 " - Parameter buffer (e.g. SC_realpath_params) can be passed by wrapping them in '[' and ']'. Note that '[' and ']' must be separate arguments to syscall(1). Buffers can be used recursively.\n" 54 " - The first argument may also be any syscall function name. Run 'syscall -l' to see the list.\n" 55 " - Arguments that cannot be interpreted are treated as string arguments, for example 'Hello, friends!'.\n" 56 "\n" 57 "Full example: syscall -o realpath [ /usr/share/man/man2/getgid.md 1024 buf 1024 ]"); 58 args_parser.add_option(list_syscalls, "List all existing syscalls, and exit", "list-syscalls", 'l'); 59 args_parser.add_option(output_buffer, "Output the contents of the buffer (beware of stray zero bytes!)", "output-buffer", 'o'); 60 args_parser.add_positional_argument(syscall_arguments, "Syscall arguments; see general help.", "syscall-arguments", Core::ArgsParser::Required::No); 61 args_parser.parse(arguments); 62 63 if (list_syscalls) { 64 outln("syscall list:"); 65 for (int sc = 0; sc < Syscall::Function::__Count; ++sc) { 66 outln(" \033[33;1m{}\033[0m - {}", sc, static_cast<Syscall::Function>(sc)); 67 } 68 exit(0); 69 } 70 71 if (syscall_arguments.is_empty()) { 72 args_parser.print_usage(stderr, arguments.strings[0]); 73 exit(1); 74 } 75 76 ArgIter iter = syscall_arguments.begin(); 77 for (size_t i = 0; i < SC_NARG && !iter.is_end(); i++) { 78 arg[i] = parse_from(iter); 79 } 80 if (!iter.is_end()) { 81 warnln("Too many arguments (did you want to use '[ parameter buffers ]'?)"); 82 return -1; 83 } 84 85 if (arg[0] > Syscall::Function::__Count) { 86 for (int sc = 0; sc < Syscall::Function::__Count; ++sc) { 87 if (Syscall::to_string((Syscall::Function)sc) == (char const*)arg[0]) { 88 arg[0] = sc; 89 break; 90 } 91 } 92 if (arg[0] > Syscall::Function::__Count) { 93 warnln("Invalid syscall entry {}", (char*)arg[0]); 94 return -1; 95 } 96 } 97 98 dbgln_if(SYSCALL_1_DEBUG, "Calling {} {:p} {:p} {:p}\n", arg[0], arg[1], arg[2], arg[3]); 99 int rc = syscall(arg[0], arg[1], arg[2], arg[3]); 100 if (output_buffer) 101 fwrite(outbuf, 1, sizeof(outbuf), stdout); 102 103 if (-rc >= 0 && -rc < EMAXERRNO) { 104 warnln("Syscall return: {} ({})", rc, strerror(-rc)); 105 } else { 106 warnln("Syscall return: {} (?)", rc); 107 } 108 return 0; 109} 110 111static FlatPtr as_buf(Vector<FlatPtr> params_vec) 112{ 113 size_t params_size = sizeof(FlatPtr) * params_vec.size(); 114 size_t buf_size = round_up_to_power_of_two(params_size + 1, PAGE_SIZE); 115 void* buf = mmap(nullptr, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0); 116 if (buf == MAP_FAILED) { 117 fprintf(stderr, "Warning: Could not allocate buffer of size %zu (low memory?)\n", buf_size); 118 exit(1); 119 } 120 // It's probably good to ensure zero-initialization. 121 memset(buf, 0, buf_size); 122 memcpy(buf, params_vec.data(), params_size); 123 124 if constexpr (SYSCALL_1_DEBUG) { 125 StringBuilder builder; 126 builder.append("Prepared ["sv); 127 for (size_t i = 0; i < params_vec.size(); ++i) { 128 builder.appendff(" {:p}", params_vec[i]); 129 } 130 builder.appendff(" ] at {:p}", (FlatPtr)buf); 131 dbgln("{}", builder.to_deprecated_string()); 132 } 133 134 // Leak the buffer here. We need to keep it until the special syscall happens, 135 // and we terminate immediately afterwards anyway. 136 return (FlatPtr)buf; 137} 138 139static FlatPtr parse_parameter_buffer(ArgIter& iter) 140{ 141 Vector<FlatPtr> params_vec; 142 while (!iter.is_end()) { 143 if (*iter == "]"sv) { 144 ++iter; 145 return as_buf(params_vec); 146 } 147 148 params_vec.append(parse_from(iter)); 149 } 150 151 fprintf(stderr, "Error: Unmatched '['?!\n"); 152 exit(1); 153 VERIFY_NOT_REACHED(); 154} 155 156static FlatPtr parse_from(ArgIter& iter) 157{ 158 auto const& this_arg_string = *iter; 159 auto* this_arg = this_arg_string.characters(); 160 ++iter; 161 162 // Is it a forced literal? 163 if (this_arg[0] == ',') { 164 this_arg += 1; 165 dbgln_if(SYSCALL_1_DEBUG, "Using (forced) string >>{}<< at {:p}", this_arg_string, (FlatPtr)this_arg); 166 return (FlatPtr)this_arg; 167 } 168 169 // Is it the output buffer? 170 if (this_arg_string == "buf"sv) 171 return (FlatPtr)outbuf; 172 173 // Is it a parameter buffer? 174 if (this_arg_string == "["sv) 175 return parse_parameter_buffer(iter); 176 177 // Is it a number? 178 if (auto l = this_arg_string.to_uint(); l.has_value()) 179 return *l; 180 181 // Then it must be a string: 182 if (this_arg_string == "]"sv) 183 fprintf(stderr, "Warning: Treating unmatched ']' as literal string\n"); 184 185 dbgln_if(SYSCALL_1_DEBUG, "Using (detected) string >>{}<< at {:p}", this_arg_string, (FlatPtr)this_arg); 186 187 return (FlatPtr)this_arg; 188}