Serenity Operating System
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}