1-- 2-- SPDX-License-Identifier: BSD-2-Clause 3-- 4-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org> 5-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com> 6-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> 7-- 8 9local config = require("config") 10local scarg = require("core.scarg") 11local scret = require("core.scret") 12local util = require("tools.util") 13 14local syscall = {} 15 16syscall.__index = syscall 17 18syscall.known_flags = util.set { 19 "STD", 20 "OBSOL", 21 "RESERVED", 22 "UNIMPL", 23 "NODEF", 24 "NOARGS", 25 "NOPROTO", 26 "NOSTD", 27 "NOTSTATIC", 28 "CAPENABLED", 29 "SYSMUX", 30} 31 32-- Native is an arbitrarily large number to have a constant and not 33-- interfere with compat numbers. 34local native = 1000000 35 36-- Processes and assigns the appropriate thread flag for this system call. 37function syscall:processThr() 38 self.thr = "SY_THR_STATIC" 39 for k, _ in pairs(self.type) do 40 if k == "NOTSTATIC" then 41 self.thr = "SY_THR_ABSENT" 42 end 43 end 44end 45 46-- Processes and assigns the appropriate capability flag for this system call. 47-- "SYF_CAPENABLED" for capability enabled; "0" for NOT capability enabled. 48function syscall:processCap() 49 self.cap = "0" 50 local stripped = util.stripAbiPrefix(self.name, self.prefix) 51 for k, _ in pairs(self.type) do 52 if k == "CAPENABLED" then 53 self.cap = "SYF_CAPENABLED" 54 end 55 end 56end 57 58-- Check that this system call has a known type. 59local function checkType(type) 60 for k, _ in pairs(type) do 61 if not syscall.known_flags[k] and not 62 k:match("^COMPAT") then 63 util.abort(1, "Bad type: " .. k) 64 end 65 end 66end 67 68-- If there are ABI changes from native, process this system call to match the 69-- target ABI. 70function syscall:processChangesAbi() 71 -- First, confirm we want to uphold our changes_abi flag. 72 if config.syscall_no_abi_change[self.name] then 73 self.changes_abi = false 74 end 75 self.noproto = not util.isEmpty(config.abi_flags) and 76 not self.changes_abi 77 if config.abiChanges("pointer_args") then 78 for _, v in ipairs(self.args) do 79 if util.isPtrType(v.type, config.abi_intptr_t) then 80 if config.syscall_no_abi_change[self.name] then 81 print("WARNING: " .. self.name .. 82 " in syscall_no_abi_change, " .. 83 "but pointers args are present") 84 end 85 self.changes_abi = true 86 goto ptrfound 87 end 88 end 89 ::ptrfound:: 90 end 91 if config.syscall_abi_change[self.name] then 92 self.changes_abi = true 93 end 94 if self.changes_abi then 95 self.noproto = false 96 end 97end 98 99-- Final processing of flags. Process any flags that haven't already been 100-- processed (e.g., dictionaries from syscalls.conf). 101function syscall:processFlags() 102 if config.obsol[self.name] or (self:compatLevel() > 0 and 103 self:compatLevel() < tonumber(config.mincompat)) then 104 self.args = nil 105 self.type.OBSOL = true 106 -- Don't apply any ABI handling, declared as obsolete. 107 self.changes_abi = false 108 end 109 if config.unimpl[self.name] then 110 self.type.UNIMPL = true 111 end 112 if self.noproto or self.type.SYSMUX then 113 self.type.NOPROTO = true 114 end 115 if self.type.NODEF then 116 self.audit = "AUE_NULL" 117 end 118end 119 120-- Returns TRUE if prefix and arg_prefix are assigned; FALSE if they're left 121-- unassigned. Relies on a valid changes_abi flag, so should be called AFTER 122-- processChangesAbi(). 123function syscall:processPrefix() 124 -- If there are ABI changes from native, assign the correct prefixes. 125 if self.changes_abi then 126 self.arg_prefix = config.abi_func_prefix 127 self.prefix = config.abi_func_prefix 128 return true 129 end 130 return false 131end 132 133-- Validate that we're not skipping system calls by comparing this system call 134-- number to the previous system call number. Called higher up the call stack 135-- by class FreeBSDSyscall. 136function syscall:validate(prev) 137 return prev + 1 == self.num 138end 139 140-- Return the compat prefix for this system call. 141function syscall:compatPrefix() 142 local c = self:compatLevel() 143 if self.type.OBSOL then 144 return "obs_" 145 end 146 if self.type.RESERVED then 147 return "reserved #" 148 end 149 if self.type.UNIMPL then 150 return "unimp_" 151 end 152 if c == 3 then 153 return "o" 154 end 155 if c < native then 156 return "freebsd" .. tostring(c) .. "_" 157 end 158 return "" 159end 160 161-- Return the symbol name for this system call. 162function syscall:symbol() 163 return self:compatPrefix() .. self.name 164end 165 166-- 167-- Return the compatibility level for this system call. 168-- 0 is obsolete. 169-- < 0 is this isn't really a system call we care about. 170-- 3 is 4.3BSD in theory, but anything before FreeBSD 4. 171-- >= 4 is FreeBSD version, this system call was replaced with a new 172-- version. 173-- 174function syscall:compatLevel() 175 if self.type.UNIMPL or self.type.RESERVED then 176 return -1 177 elseif self.type.OBSOL then 178 return 0 179 elseif self.type.COMPAT then 180 return 3 181 end 182 for k, _ in pairs(self.type) do 183 local l = k:match("^COMPAT(%d+)") 184 if l ~= nil then 185 return tonumber(l) 186 end 187 end 188 return native 189end 190 191-- Adds the definition for this system call. Guarded by whether we already have 192-- a system call number or not. 193function syscall:addDef(line) 194 if self.num == nil then 195 local words = util.split(line, "%S+") 196 self.num = words[1] 197 self.audit = words[2] 198 self.type = util.setFromString(words[3], "[^|]+") 199 checkType(self.type) 200 self.name = words[4] 201 -- These next three are optional, and either all present 202 -- or all absent. 203 self.altname = words[5] 204 self.alttag = words[6] 205 self.rettype = words[7] 206 return true 207 end 208 return false 209end 210 211-- Adds the function declaration for this system call. If addDef() found an 212-- opening curly brace, then we're looking for a function declaration. 213function syscall:addFunc(line) 214 if self.name == "{" then 215 local words = util.split(line, "%S+") 216 -- Expect line is `type syscall(` or `type syscall(void);`. 217 if #words ~= 2 then 218 util.abort(1, "Malformed line " .. line) 219 end 220 221 local ret = scret:new({}, line) 222 self.ret = ret:add() 223 -- Don't clobber rettype set in the alt information. 224 if self.rettype == nil then 225 self.rettype = "int" 226 end 227 228 self.name = words[2]:match("([%w_]+)%(") 229 if words[2]:match("%);$") then 230 -- Now we're looking for ending curly brace. 231 self.expect_rbrace = true 232 end 233 return true 234 end 235 return false 236end 237 238-- Adds the argument(s) for this system call. Once addFunc() assigns a name 239-- for this system call, arguments are next in syscalls.master. 240function syscall:addArgs(line) 241 if not self.expect_rbrace then 242 if line:match("%);$") then 243 self.expect_rbrace = true 244 return true 245 end 246 local arg = scarg:new({}, line) 247 -- We don't want to add this argument if it doesn't process. 248 -- scarg:process() handles those conditions. 249 if arg:process() then 250 arg:append(self.args) 251 end 252 -- If this argument has ABI changes, set globally for this 253 -- system call. 254 self.changes_abi = self.changes_abi or arg:changesAbi() 255 return true 256 end 257 return false 258end 259 260-- Once we have a good syscall, add some final information to it. 261function syscall:finalize() 262 if self.name == nil then 263 self.name = "" 264 end 265 266 -- Preserve the original name as the alias. 267 self.alias = self.name 268 269 self:processChangesAbi() -- process changes to the ABI 270 self:processFlags() -- process any unprocessed flags 271 272 -- If there's changes to the ABI, these prefixes will be changed by 273 -- processPrefix(); otherwise, they'll remain empty. 274 self.prefix = "" 275 self.arg_prefix = "" 276 self:processPrefix() 277 278 self:processCap() -- capability flag 279 self:processThr() -- thread flag 280 281 -- Assign argument alias. 282 if self.alttag ~= nil then 283 self.arg_alias = self.alttag 284 elseif self.arg_alias == nil and self.name ~= nil then 285 -- argalias should be: 286 -- COMPAT_PREFIX + ABI Prefix + funcname 287 self.arg_alias = self:compatPrefix() .. self.arg_prefix .. 288 self.name .. "_args" 289 elseif self.arg_alias ~= nil then 290 self.arg_alias = self.arg_prefix .. self.arg_alias 291 end 292 293 -- An empty string would not want a prefix; the entry doesn't have 294 -- a name so we want to keep the empty string. 295 if self.name ~= nil and self.name ~= "" then 296 self.name = self.prefix .. self.name 297 end 298 299 self:processArgstrings() 300 self:processArgsize() 301end 302 303-- Assigns the correct args_size. Defaults to "0", except if there's arguments 304-- or NODEF flag. 305function syscall:processArgsize() 306 if self.type.SYSMUX then -- catch this first 307 self.args_size = "0" 308 elseif self.arg_alias ~= nil and 309 (#self.args ~= 0 or self.type.NODEF) then 310 self.args_size = "AS(" .. self.arg_alias .. ")" 311 else 312 self.args_size = "0" 313 end 314end 315 316-- Constructs argstr_* strings for generated declerations/wrappers. 317function syscall:processArgstrings() 318 local type = "" 319 local type_var = "" 320 local var = "" 321 local comma = "" 322 323 for _, v in ipairs(self.args) do 324 local argname, argtype = v.name, v.type 325 type = type .. comma .. argtype 326 type_var = type_var .. comma .. argtype .. " " .. argname 327 var = var .. comma .. argname 328 comma = ", " 329 end 330 if type == "" then 331 type = "void" 332 type_var = "void" 333 end 334 335 self.argstr_type = type 336 self.argstr_type_var = type_var 337 self.argstr_var = var 338end 339 340-- Interface to add this system call to the master system call table. 341-- The system call is built up one line at a time. The states describe the 342-- current parsing state. 343-- Returns TRUE when ready to add and FALSE while still parsing. 344function syscall:add(line) 345 if self:addDef(line) then 346 return self:isAdded(line) 347 end 348 if self:addFunc(line) then 349 return false -- Function added; keep going. 350 end 351 if self:addArgs(line) then 352 return false -- Arguments added; keep going. 353 end 354 return self:isAdded(line) -- Final validation, before adding. 355end 356 357-- Returns TRUE if this system call was succesfully added. There's two entry 358-- points to this function: (1) the entry in syscalls.master is one-line, or 359-- (2) the entry is a full system call. This function handles those cases and 360-- decides whether to exit early for (1) or validate a full system call for 361-- (2). This function also handles cases where we don't want to add, and 362-- instead want to abort. 363function syscall:isAdded(line) 364 -- This system call is a range - exit early. 365 if tonumber(self.num) == nil then 366 -- The only allowed types are RESERVED and UNIMPL. 367 if not (self.type.RESERVED or self.type.UNIMPL) then 368 util.abort(1, "Range only allowed with RESERVED " .. 369 "and UNIMPL: " .. line) 370 end 371 self:finalize() 372 return true 373 -- This system call is a loadable system call - exit early. 374 elseif self.altname ~= nil and self.alttag ~= nil and 375 self.rettype ~= nil then 376 self:finalize() 377 return true 378 -- This system call is only one line, and should only be one line 379 -- (we didn't make it to addFunc()) - exit early. 380 elseif self.name ~= "{" and self.ret == nil then 381 self:finalize() 382 return true 383 -- This is a full system call and we've passed multiple states to 384 -- get here - final exit. 385 elseif self.expect_rbrace then 386 if not line:match("}$") then 387 util.abort(1, "Expected '}' found '" .. line .. 388 "' instead.") 389 end 390 self:finalize() 391 return true 392 end 393 return false 394end 395 396-- Return TRUE if this system call is native. 397function syscall:native() 398 return self:compatLevel() == native 399end 400 401-- Make a shallow copy of `self` and replace the system call number with num 402-- (which should be a number). 403-- For system call ranges. 404function syscall:shallowCopy(num) 405 local obj = syscall:new() 406 407 -- shallow copy 408 for k, v in pairs(self) do 409 obj[k] = v 410 end 411 obj.num = num -- except override range 412 return obj 413end 414 415-- Make a deep copy of the parameter object. Save copied tables in `copies`, 416-- indexed by original table. 417-- CREDIT: http://lua-users.org/wiki/CopyTable 418-- For a full system call (the nested arguments table should be a deep copy). 419local function deepCopy(orig, copies) 420 copies = copies or {} 421 local orig_type = type(orig) 422 local copy 423 if orig_type == 'table' then 424 if copies[orig] then 425 copy = copies[orig] 426 else 427 copy = {} 428 copies[orig] = copy 429 for orig_key, orig_value in next, orig, nil do 430 copy[deepCopy(orig_key, copies)] = 431 deepCopy(orig_value, copies) 432 end 433 setmetatable(copy, deepCopy(getmetatable(orig), copies)) 434 end 435 else -- number, string, boolean, etc 436 copy = orig 437 end 438 return copy 439end 440 441-- 442-- In syscalls.master, system calls come in two types: (1) a fully defined 443-- system call with function declaration, with a distinct number for each system 444-- call; or (2) a one-line entry, sometimes with a distinct number and sometimes 445-- with a range of numbers. One-line entries can be obsolete, reserved, no 446-- definition, etc. Ranges are only allowed for reserved and unimplemented. 447-- 448-- This function provides the iterator to traverse system calls by number. If 449-- the entry is a fully defined system call with a distinct number, the iterator 450-- creates a deep copy and captures any nested objects; if the entry is a range 451-- of numbers, the iterator creates shallow copies from the start of the range 452-- to the end of the range. 453-- 454function syscall:iter() 455 local s = tonumber(self.num) 456 local e 457 if s == nil then 458 s, e = string.match(self.num, "(%d+)%-(%d+)") 459 s, e = tonumber(s), tonumber(e) 460 return function () 461 if s <= e then 462 s = s + 1 463 return self:shallowCopy(s - 1) 464 end 465 end 466 else 467 e = s 468 self.num = s -- Replace string with number, like the clones. 469 return function () 470 if s == e then 471 local deep_copy = deepCopy(self) 472 s = e + 1 473 return deep_copy 474 end 475 end 476 end 477end 478 479function syscall:new(obj) 480 obj = obj or { } 481 setmetatable(obj, self) 482 self.__index = self 483 484 self.expect_rbrace = false 485 self.changes_abi = false 486 self.args = {} 487 self.noproto = false 488 489 return obj 490end 491 492return syscall 493