From 7ab15e0b236d085c822aac3e1ffa46c48c62c3fa Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 14 Aug 2022 20:16:44 -0700 Subject: [PATCH] Add .PLEDGE/.CPU/.MEMORY/etc. to Landlock Make 1.2 --- ape/loader.c | 115 +++++-- ape/loader.lds | 3 +- examples/sysinfo.c | 18 +- libc/calls/pledge-linux.c | 3 +- libc/fmt/formatmemorysize.c | 26 +- libc/fmt/itoa.h | 2 +- {net/http => libc/intrin}/isloopbackip.c | 0 {net/http => libc/intrin}/isprivateip.c | 0 libc/{sock => intrin}/ispublicip.c | 0 libc/sock/nointernet.c | 2 +- test/libc/sock/setsockopt_test.c | 2 +- third_party/make/README | 188 ------------ third_party/make/README.cosmo | 29 +- third_party/make/function.c | 2 +- third_party/make/job.c | 365 ++++++++++++++++++++--- third_party/make/job.h | 2 +- third_party/make/main.c | 50 ++-- tool/build/lib/syscall.c | 10 +- tool/net/redbean.c | 2 +- tool/viz/memplan.c | 4 +- 20 files changed, 494 insertions(+), 329 deletions(-) rename {net/http => libc/intrin}/isloopbackip.c (100%) rename {net/http => libc/intrin}/isprivateip.c (100%) rename libc/{sock => intrin}/ispublicip.c (100%) delete mode 100644 third_party/make/README diff --git a/ape/loader.c b/ape/loader.c index fa5f85e77..33088d176 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/loader.h" +#define SET_EXE_FILE 0 /* needs root ;_; */ #define TROUBLESHOOT 0 #define TROUBLESHOOT_OS LINUX @@ -99,30 +100,32 @@ #define IsOpenbsd() (SupportsOpenbsd() && os == OPENBSD) #define IsNetbsd() (SupportsNetbsd() && os == NETBSD) -#define O_RDONLY 0 -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_FIXED 16 -#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096) -#define AT_EXECFN_LINUX 31 -#define AT_EXECFN_NETBSD 2014 -#define ELFCLASS64 2 -#define ELFDATA2LSB 1 -#define EM_NEXGEN32E 62 -#define ET_EXEC 2 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define EI_CLASS 4 -#define EI_DATA 5 -#define PF_X 1 -#define PF_W 2 -#define PF_R 4 -#define X_OK 1 -#define XCR0_SSE 2 -#define XCR0_AVX 4 +#define O_RDONLY 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_FIXED 16 +#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096) +#define AT_EXECFN_LINUX 31 +#define AT_EXECFN_NETBSD 2014 +#define ELFCLASS64 2 +#define ELFDATA2LSB 1 +#define EM_NEXGEN32E 62 +#define ET_EXEC 2 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define EI_CLASS 4 +#define EI_DATA 5 +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 +#define X_OK 1 +#define XCR0_SSE 2 +#define XCR0_AVX 4 +#define PR_SET_MM 35 +#define PR_SET_MM_EXE_FILE 13 #define Read32(S) \ ((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \ @@ -174,7 +177,10 @@ struct ElfPhdr { unsigned long p_align; }; +extern char ehdr[]; +extern char _end[]; static void *syscall; +static char relocated; static struct PathSearcher ps; extern char __syscall_loader[]; @@ -182,7 +188,7 @@ static int ToLower(int c) { return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c; } -static char *MemCpy(char *d, const char *s, unsigned long n) { +char *MemCpy(char *d, const char *s, unsigned long n) { unsigned long i = 0; for (; i < n; ++i) d[i] = s[i]; return d + n; @@ -373,6 +379,26 @@ __attribute__((__noinline__)) static long Mmap(long addr, long size, int prot, return ax; } +static int MunmapLinux(const void *addr, unsigned long size) { + int ax; + asm volatile("syscall" + : "=a"(ax) + : "0"(11), "D"(addr), "S"(size) + : "rcx", "r11", "memory"); + return ax; +} + +static int PrctlLinux(int op, long a, long b, long c, long d) { + int rc; + asm volatile("mov\t%5,%%r10\n\t" + "mov\t%6,%%r8\n\t" + "syscall" + : "=a"(rc) + : "0"(157), "D"(op), "S"(a), "d"(b), "g"(c), "g"(d) + : "rcx", "r8", "r10", "r11", "memory"); + return rc; +} + static void Emit(int os, const char *s) { Write(2, s, StrLen(s), os); } @@ -539,6 +565,23 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, if (!code) { Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X"); } + +#if SET_EXE_FILE + // change /proc/pid/exe to new executable path + if (IsLinux() && relocated) { + MunmapLinux((char *)0x200000, (long)(_end - ehdr)); + PrctlLinux(PR_SET_MM, PR_SET_MM_EXE_FILE, fd, 0, 0); + } +#endif + + if (relocated) { + int ax; + asm volatile("syscall" + : "=a"(ax) + : "0"(1), "D"(1), "S"("hello\n"), "d"(6) + : "rcx", "r11", "memory"); + } + Close(fd, os); // authorize only the loaded program to issue system calls. if this @@ -584,7 +627,7 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, struct ApeLoader *handoff) { int rc; long *auxv; - struct ElfEhdr *ehdr; + struct ElfEhdr *eh; int c, i, fd, os, argc; char *p, *exe, *prog, **argv, **envp, *page; static union { @@ -630,7 +673,7 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, fd = handoff->fd; exe = handoff->prog; page = handoff->page; - ehdr = (struct ElfEhdr *)handoff->page; + eh = (struct ElfEhdr *)handoff->page; } else { // detect openbsd @@ -653,6 +696,20 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, os = LINUX; } +#if SET_EXE_FILE + if (IsLinux() && !relocated) { + char *b1 = (char *)0x200000; + char *b2 = (char *)0x300000; + void (*pApeLoader)(long, long *, char, struct ApeLoader *); + Mmap((long)b2, (long)(_end - ehdr), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, os); + relocated = 1; + MemCpy(b2, b1, (long)(_end - ehdr)); + pApeLoader = (void *)((char *)&ApeLoader - b1 + b2); + pApeLoader(di, sp, dl, handoff); + } +#endif + // we can load via shell, shebang, or binfmt_misc if (argc >= 3 && !StrCmp(argv[1], "-")) { // if the first argument is a hyphen then we give the user the @@ -686,7 +743,7 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, } page = u.p; - ehdr = &u.ehdr; + eh = &u.ehdr; } #if TROUBLESHOOT @@ -726,7 +783,7 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, page[i++] = c; } if (i >= 64 && Read32(page) == Read32("\177ELF")) { - Spawn(os, exe, fd, sp, page, ehdr); + Spawn(os, exe, fd, sp, page, eh); } } diff --git a/ape/loader.lds b/ape/loader.lds index b689cbae0..0e3488386 100644 --- a/ape/loader.lds +++ b/ape/loader.lds @@ -32,7 +32,8 @@ SECTIONS { *(.bss) . = ALIGN(4096); } - memsz = . - ehdr; + _end = .; + memsz = _end - ehdr; /DISCARD/ : { *(.*) } diff --git a/examples/sysinfo.c b/examples/sysinfo.c index 568c64fa8..10b3cca8b 100644 --- a/examples/sysinfo.c +++ b/examples/sysinfo.c @@ -45,33 +45,33 @@ int main(int argc, char *argv[]) { 1. / 65536 * si.loads[1], // 1. / 65536 * si.loads[2]); // - FormatMemorySize(ibuf, si.totalram); + FormatMemorySize(ibuf, si.totalram, 1024); printf("%-16s %s\n", "totalram", ibuf); - FormatMemorySize(ibuf, si.freeram); + FormatMemorySize(ibuf, si.freeram, 1024); printf("%-16s %s\n", "freeram", ibuf); - FormatMemorySize(ibuf, si.sharedram); + FormatMemorySize(ibuf, si.sharedram, 1024); printf("%-16s %s\n", "sharedram", ibuf); - FormatMemorySize(ibuf, si.bufferram); + FormatMemorySize(ibuf, si.bufferram, 1024); printf("%-16s %s\n", "bufferram", ibuf); - FormatMemorySize(ibuf, si.totalswap); + FormatMemorySize(ibuf, si.totalswap, 1024); printf("%-16s %s\n", "totalswap", ibuf); - FormatMemorySize(ibuf, si.freeswap); + FormatMemorySize(ibuf, si.freeswap, 1024); printf("%-16s %s\n", "freeswap", ibuf); printf("%-16s %lu\n", "processes", si.procs); - FormatMemorySize(ibuf, si.totalhigh); + FormatMemorySize(ibuf, si.totalhigh, 1024); printf("%-16s %s\n", "totalhigh", ibuf); - FormatMemorySize(ibuf, si.freehigh); + FormatMemorySize(ibuf, si.freehigh, 1024); printf("%-16s %s\n", "freehigh", ibuf); - FormatMemorySize(ibuf, si.mem_unit); + FormatMemorySize(ibuf, si.mem_unit, 1024); printf("%-16s %s\n", "mem_unit", ibuf); // diff --git a/libc/calls/pledge-linux.c b/libc/calls/pledge-linux.c index 848f80a33..192177ab8 100644 --- a/libc/calls/pledge-linux.c +++ b/libc/calls/pledge-linux.c @@ -825,7 +825,7 @@ static privileged void *MemCpy(void *d, const void *s, unsigned long n) { return (char *)d + n; } -static privileged char *FixCpy(char p[17], uint64_t x, uint8_t k) { +static privileged char *FixCpy(char p[17], uint64_t x, int k) { while (k > 0) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15]; *p = '\0'; return p; @@ -868,7 +868,6 @@ static privileged void Log(const char *s, ...) { static privileged int Prctl(int op, long a, void *b, long c, long d) { int rc; - va_list va; asm volatile("mov\t%5,%%r10\n\t" "mov\t%6,%%r8\n\t" "syscall" diff --git a/libc/fmt/formatmemorysize.c b/libc/fmt/formatmemorysize.c index 79ecfba48..44a30d8d0 100644 --- a/libc/fmt/formatmemorysize.c +++ b/libc/fmt/formatmemorysize.c @@ -19,26 +19,26 @@ #include "libc/fmt/itoa.h" #include "libc/macros.internal.h" -static const struct { - char suffix; - uint64_t size; -} kUnits[] = { - {'e', 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024}, - {'p', 1024ULL * 1024 * 1024 * 1024 * 1024}, - {'t', 1024ULL * 1024 * 1024 * 1024}, - {'g', 1024ULL * 1024 * 1024}, - {'m', 1024ULL * 1024}, - {'k', 1024ULL}, -}; - /** * Represents size of memory readably. * * @param p is output buffer + * @param b should be 1024 or 1000 * @return pointer to nul byte */ -char *FormatMemorySize(char *p, uint64_t x) { +char *FormatMemorySize(char *p, uint64_t x, uint64_t b) { int i, suffix; + struct { + char suffix; + uint64_t size; + } kUnits[] = { + {'e', b * b * b * b * b * b}, + {'p', b * b * b * b * b}, + {'t', b * b * b * b}, + {'g', b * b * b}, + {'m', b * b}, + {'k', b}, + }; for (suffix = i = 0; i < ARRAYLEN(kUnits); ++i) { if (x >= kUnits[i].size * 9) { x = (x + kUnits[i].size / 2) / kUnits[i].size; diff --git a/libc/fmt/itoa.h b/libc/fmt/itoa.h index b63f91b21..7416ee58d 100644 --- a/libc/fmt/itoa.h +++ b/libc/fmt/itoa.h @@ -21,7 +21,7 @@ char *FormatFlex64(char[hasatleast 24], int64_t, char); size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]); size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t); size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]); -char *FormatMemorySize(char *, uint64_t); +char *FormatMemorySize(char *, uint64_t, uint64_t); #ifndef __STRICT_ANSI__ size_t int128toarray_radix10(int128_t, char *); diff --git a/net/http/isloopbackip.c b/libc/intrin/isloopbackip.c similarity index 100% rename from net/http/isloopbackip.c rename to libc/intrin/isloopbackip.c diff --git a/net/http/isprivateip.c b/libc/intrin/isprivateip.c similarity index 100% rename from net/http/isprivateip.c rename to libc/intrin/isprivateip.c diff --git a/libc/sock/ispublicip.c b/libc/intrin/ispublicip.c similarity index 100% rename from libc/sock/ispublicip.c rename to libc/intrin/ispublicip.c diff --git a/libc/sock/nointernet.c b/libc/sock/nointernet.c index 73a3a6083..b26530aeb 100644 --- a/libc/sock/nointernet.c +++ b/libc/sock/nointernet.c @@ -158,7 +158,7 @@ static bool IsSockaddrAllowed(struct sockaddr_storage *addr) { } if (addr->ss_family == AF_INET) { ip = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); - if (!IsPublicIp(ip)) { + if (IsPrivateIp(ip) || IsLoopbackIp(ip)) { return true; } else { kprintf("warning: attempted to communicate with public ip " diff --git a/test/libc/sock/setsockopt_test.c b/test/libc/sock/setsockopt_test.c index 87ef63367..b8250c66a 100644 --- a/test/libc/sock/setsockopt_test.c +++ b/test/libc/sock/setsockopt_test.c @@ -35,7 +35,7 @@ void SetUpOnce(void) { TEST(setsockopt, SO_RCVTIMEO) { char buf[32]; struct timeval tv = {0, 10000}; - struct sockaddr_in sa = {AF_INET}; + struct sockaddr_in sa = {AF_INET, 0, {htonl(0x7f000001)}}; EXPECT_SYS(0, 3, socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); EXPECT_SYS(0, 0, setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))); EXPECT_SYS(0, 0, bind(3, &sa, sizeof(struct sockaddr_in))); diff --git a/third_party/make/README b/third_party/make/README deleted file mode 100644 index 9449748aa..000000000 --- a/third_party/make/README +++ /dev/null @@ -1,188 +0,0 @@ -This directory contains the 4.3 release of GNU Make. - -See the file NEWS for the user-visible changes from previous releases. -In addition, there have been bugs fixed. - -Please check the system-specific notes below for any caveats related to your -operating system. - -If you are trying to build GNU make from a Git clone rather than a downloaded -source distribution, see the README.git file for instructions. - -For source distribution building and installation instructions, see the file -INSTALL. - -If you need to build GNU Make and have no other 'make' program to use, you can -use the shell script 'build.sh' instead. To do this, first run 'configure' as -described in INSTALL. Then, instead of typing 'make' to build the program, -type 'sh build.sh'. This should compile the program in the current directory. -Then you will have a Make program that you can use for './make install', or -whatever else. - -Some systems' Make programs cannot process the Makefile for GNU Make. If you -get errors from your system's Make when building GNU Make, try using -'build.sh' instead. - - -GNU Make is free software. See the file COPYING for copying conditions. -GNU Make is copyright by the Free Software Foundation. Copyright notices -condense sequential years into a range; e.g. "1987-1994" means all years -from 1987 to 1994 inclusive. - -Downloading ------------ - -GNU Make can be obtained in many different ways. See a description here: - - http://www.gnu.org/software/software.html - - -Documentation -------------- - -GNU make is fully documented in the GNU Make manual, which is contained -in this distribution as the file make.texinfo. You can also find -on-line and preformatted (PostScript and DVI) versions at the FSF's web -site. There is information there about ordering hardcopy documentation. - - http://www.gnu.org/ - http://www.gnu.org/doc/doc.html - http://www.gnu.org/manual/manual.html - - -Development ------------ - -GNU Make development is hosted by Savannah, the FSF's online development -management tool. Savannah is here: - - http://savannah.gnu.org - -And the GNU Make development page is here: - - http://savannah.gnu.org/projects/make/ - -You can find most information concerning the development of GNU Make at -this site. - - -Bug Reporting -------------- - -You can send GNU make bug reports to . Please see the -section of the GNU make manual entitled 'Problems and Bugs' for -information on submitting useful and complete bug reports. - -You can also use the online bug tracking system in the Savannah GNU Make -project to submit new problem reports or search for existing ones: - - http://savannah.gnu.org/bugs/?group=make - -If you need help using GNU make, try these forums: - - help-make@gnu.org - help-utils@gnu.org - news:gnu.utils.help - news:gnu.utils.bug - - -Git Access ----------- - -The GNU make source repository is available via Git from the GNU Savannah Git -server; look here for details: - - http://savannah.gnu.org/git/?group=make - -Please note: you won't be able to build GNU make from Git without installing -appropriate maintainer's tools, such as GNU m4, automake, autoconf, Perl, GNU -make, and GCC. - -See the README.git file for instructions on how to build GNU make once these -tools are available. We make no guarantees about the contents or quality of -the latest code in the Git repository: it is not unheard of for code that is -known to be broken to be checked in. Use at your own risk. - - -System-specific Notes ---------------------- - -It has been reported that the XLC 1.2 compiler on AIX 3.2 is buggy such -that if you compile make with 'cc -O' on AIX 3.2, it will not work -correctly. It is said that using 'cc' without '-O' does work. - -The standard /bin/sh on SunOS 4.1.3_U1 and 4.1.4 is broken and cannot be -used to configure GNU make. Please install a different shell such as -bash or pdksh in order to run "configure". See this message for more -information: - http://mail.gnu.org/archive/html/bug-autoconf/2003-10/msg00190.html - -One area that is often a problem in configuration and porting is the code -to check the system's current load average. To make it easier to test and -debug this code, you can do 'make check-loadavg' to see if it works -properly on your system. (You must run 'configure' beforehand, but you -need not build Make itself to run this test.) - -Another potential source of porting problems is the support for large -files (LFS) in configure for those operating systems that provide it. -Please report any bugs that you find in this area. If you run into -difficulties, then as a workaround you should be able to disable LFS by -adding the '--disable-largefile' option to the 'configure' script. - -On systems that support micro- and nano-second timestamp values and -where stat(2) provides this information, GNU make will use it when -comparing timestamps to get the most accurate possible result. However, -note that many current implementations of tools that *set* timestamps do -not preserve micro- or nano-second granularity. This means that "cp -p" -and other similar tools (tar, etc.) may not exactly duplicate timestamps -with micro- and nano-second granularity on some systems. If your build -system contains rules that depend on proper behavior of tools like "cp --p", you should consider using the .LOW_RESOLUTION_TIME pseudo-target to -force make to treat them properly. See the manual for details. - - -Ports ------ - - - See README.customs for details on integrating GNU make with the - Customs distributed build environment from the Pmake distribution. - - - See README.VMS for details about GNU Make on OpenVMS. - - - See README.Amiga for details about GNU Make on AmigaDOS. - - - See README.W32 for details about GNU Make on Windows NT, 95, or 98. - - - See README.DOS for compilation instructions on MS-DOS and MS-Windows - using DJGPP tools. - - A precompiled binary of the MSDOS port of GNU Make is available as part - of DJGPP; see the WWW page http://www.delorie.com/djgpp/ for more - information. - - The Cygwin project maintains its own port of GNU make. That port may have - patches which are not present in this version. If you are using Cygwin - you should use their version of GNU make, and if you have questions about - it you should start by asking on those mailing lists and forums. - -Please note there are two _separate_ ports of GNU make for Microsoft -systems: a native Windows tool built with (for example) MSVC or Cygwin, -and a DOS-based tool built with DJGPP. Please be sure you are looking -at the right README! - - -------------------------------------------------------------------------------- -Copyright (C) 1988-2020 Free Software Foundation, Inc. -This file is part of GNU Make. - -GNU Make is free software; you can redistribute it and/or modify it under the -terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . diff --git a/third_party/make/README.cosmo b/third_party/make/README.cosmo index 6e452fc11..1549174df 100644 --- a/third_party/make/README.cosmo +++ b/third_party/make/README.cosmo @@ -1,12 +1,29 @@ +DESCRIPTION + + Landlock Make is a fork of GNU Make that adds support for automatic + sandboxing, resource limits, and network access restrictions. + ORIGIN - GNU Make 4.3 - http://ftp.gnu.org/gnu/make/make-4.3.tar.gz + GNU Make 4.3 + http://ftp.gnu.org/gnu/make/make-4.3.tar.gz + +LICENSE + + GNU GPL version 3 or later + http://gnu.org/licenses/gpl.html LOCAL CHANGES - - Remove portability torture code - - Add support for Landlock LSM sandboxing - - Add .UNSANDBOXED variable to disable it - - Add .UNVEIL variable which works like .EXTRA_PREREQS + - .INTERNET variable to allow internet access + - .PLEDGE variable which restricts system calls + - .UNVEIL variable which controls Landlock LSM + - .STRICT variable to disable implicit unveiling + - .UNSANDBOXED variable to disable pledge / unveil + - .CPU variable which tunes CPU rlimit in seconds + - .MEMORY variable for virtual memory limit, e.g. 512m + - .FSIZE variable which tunes max file size, e.g. 1g + - .NPROC variable which tunes fork() / clone() limit + - Do automatic setup and teardown of TMPDIR per rule - Remove code that forces slow path if not using /bin/sh + - Remove 200,000 lines of VAX/OS2/DOS/AMIGA/etc. code diff --git a/third_party/make/function.c b/third_party/make/function.c index 35adaeb4f..da5fd1815 100644 --- a/third_party/make/function.c +++ b/third_party/make/function.c @@ -1640,7 +1640,7 @@ func_shell_base (char *o, char **argv, int trim_newlines) child.output.err = errfd; child.environment = envp; - pid = child_execute_job (&child, 1, command_argv); + pid = child_execute_job (&child, 1, command_argv, false); free (child.cmd_name); } diff --git a/third_party/make/job.c b/third_party/make/job.c index 2b475af83..a24ff4edb 100644 --- a/third_party/make/job.c +++ b/third_party/make/job.c @@ -22,23 +22,31 @@ this program. If not, see . */ /**/ #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/pledge.h" +#include "libc/calls/pledge.internal.h" #include "libc/calls/struct/bpf.h" #include "libc/calls/struct/filter.h" #include "libc/calls/struct/seccomp.h" +#include "libc/calls/struct/sysinfo.h" #include "libc/calls/struct/timeval.h" +#include "libc/dce.h" #include "libc/elf/def.h" #include "libc/elf/elf.h" #include "libc/elf/struct/ehdr.h" #include "libc/elf/struct/phdr.h" +#include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/bits.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/promises.internal.h" #include "libc/intrin/safemacros.internal.h" #include "libc/log/backtrace.internal.h" #include "libc/log/log.h" #include "libc/log/rop.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/kcpuids.h" #include "libc/runtime/runtime.h" -#include "libc/runtime/stack.h" #include "libc/sock/sock.h" #include "libc/str/str.h" #include "libc/sysv/consts/audit.h" @@ -49,6 +57,7 @@ this program. If not, see . */ #include "libc/sysv/consts/prot.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "third_party/libcxx/math.h" #include "third_party/make/commands.h" #include "third_party/make/dep.h" #include "third_party/make/os.h" @@ -75,8 +84,6 @@ int batch_mode_shell = 0; #define WAIT_T int -bool g_strict; - /* Different systems have different requirements for pid_t. Plus we have to support gettext string translation... Argh. */ static const char * @@ -377,19 +384,39 @@ child_error (struct child *child, /* [jart] manage temporary directories per rule */ +bool +parse_bool (const char *s) +{ + while (isspace (*s)) + ++s; + if (isdigit (*s)) + return !! atoi (s); + return startswithi (s, "true"); +} + +const char * +get_target_variable (const char *name, + size_t length, + struct file *file, + const char *dflt) +{ + const struct variable *var; + if ((file && + ((var = lookup_variable_in_set (name, length, + file->variables->set)) || + (file->pat_variables && + (var = lookup_variable_in_set (name, length, + file->pat_variables->set))))) || + (var = lookup_variable (name, length))) + return variable_expand (var->value); + else + return dflt; +} + const char * get_tmpdir (struct file *file) { - const struct variable *var; - if ((var = lookup_variable_in_set (STRING_SIZE_TUPLE("TMPDIR"), - file->variables->set)) || - (file->pat_variables && - (var = lookup_variable_in_set (STRING_SIZE_TUPLE("TMPDIR"), - file->pat_variables->set))) || - (var = lookup_variable (STRING_SIZE_TUPLE("TMPDIR")))) - return variable_expand (var->value); - else - return kTmpPath; + return get_target_variable (STRING_SIZE_TUPLE("TMPDIR"), file, 0); } char * @@ -1154,7 +1181,7 @@ start_job_command (struct child *child) parent_environ = environ; jobserver_pre_child (flags & COMMANDS_RECURSE); child->pid = child_execute_job ((struct childbase *)child, - child->good_stdin, argv); + child->good_stdin, argv, true); environ = parent_environ; /* Restore value child may have clobbered. */ jobserver_post_child (flags & COMMANDS_RECURSE); } @@ -1277,7 +1304,8 @@ new_job (struct file *file) c->sh_batch_file = NULL; /* [jart] manage temporary directories per rule */ - if ((c->tmpdir = new_tmpdir (get_tmpdir (file), file))) + if ((c->tmpdir = get_tmpdir (file)) && + (c->tmpdir = new_tmpdir (c->tmpdir, file))) { var = define_variable_for_file ("TMPDIR", 6, c->tmpdir, o_override, 0, file); @@ -1764,22 +1792,98 @@ unveil_variable (const struct variable *var) return -1; } -bool -Vartoi (const struct variable *var) +int +get_base_cpu_freq_mhz (void) { - return var && atoi (variable_expand (var->value)); + return KCPUIDS(16H, EAX) & 0x7fff; } +void +set_cpu_limit (int secs) +{ + int mhz, lim; + struct rlimit rlim; + if (secs <= 0) return; + if (IsWindows()) return; + if (!(mhz = get_base_cpu_freq_mhz())) return; + lim = ceil(3100. / mhz * secs); + rlim.rlim_cur = lim; + rlim.rlim_max = lim + 1; + if (setrlimit(RLIMIT_CPU, &rlim) == -1) + { + if (getrlimit(RLIMIT_CPU, &rlim) == -1) + return; + if (lim < rlim.rlim_cur) + { + rlim.rlim_cur = lim; + setrlimit(RLIMIT_CPU, &rlim); + } + } +} + +void +set_fsz_limit (long n) +{ + struct rlimit rlim; + if (n <= 0) return; + if (IsWindows()) return; + rlim.rlim_cur = n; + rlim.rlim_max = n << 1; + if (setrlimit(RLIMIT_FSIZE, &rlim) == -1) + { + if (getrlimit(RLIMIT_FSIZE, &rlim) == -1) + return; + rlim.rlim_cur = n; + setrlimit(RLIMIT_FSIZE, &rlim); + } +} + +void +set_mem_limit (long n) +{ + struct rlimit rlim = {n, n}; + if (n <= 0) return; + if (IsWindows() || IsXnu()) return; + setrlimit(RLIMIT_AS, &rlim); +} + +void +set_pro_limit (long n) +{ + struct rlimit rlim = {n, n}; + if (n <= 0) return; + setrlimit(RLIMIT_NPROC, &rlim); +} + +static struct sysinfo g_sysinfo; + +__attribute__((__constructor__)) static void +get_sysinfo (void) +{ + int e = errno; + sysinfo (&g_sysinfo); + errno = e; +} + +static bool internet; +static char *promises; + /* POSIX: Create a child process executing the command in ARGV. Returns the PID or -1. */ pid_t -child_execute_job (struct childbase *child, int good_stdin, char **argv) +child_execute_job (struct childbase *child, + int good_stdin, + char **argv, + bool is_build_rule) { const int fdin = good_stdin ? FD_STDIN : get_bad_stdin (); struct dep *d; + bool strict; bool sandboxed; + bool unsandboxed; struct child *c; + unsigned long ipromises; char pathbuf[PATH_MAX]; char outpathbuf[PATH_MAX]; int fdout = FD_STDOUT; @@ -1808,28 +1912,145 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) if (stack_limit.rlim_cur) setrlimit (RLIMIT_STACK, &stack_limit); - g_strict = Vartoi (lookup_variable (STRING_SIZE_TUPLE (".STRICT"))); - - intptr_t loc = (intptr_t)child; /* we can cast if it's on the heap ;_; */ - if (!(GetStackAddr() < loc && loc < GetStackAddr() + GetStackSize())) { + /* Tell build rules apart from $(shell foo). */ + if (is_build_rule) { c = (struct child *)child; } else { c = 0; } - sandboxed = ( - !Vartoi (lookup_variable - (STRING_SIZE_TUPLE(".UNSANDBOXED"))) && - (!c || !Vartoi (lookup_variable_in_set - (STRING_SIZE_TUPLE(".UNSANDBOXED"), - c->file->variables->set))) && - (!c || !c->file->pat_variables || - !Vartoi (lookup_variable_in_set - (STRING_SIZE_TUPLE(".UNSANDBOXED"), - c->file->pat_variables->set)))); + internet = parse_bool (get_target_variable + (STRING_SIZE_TUPLE(".INTERNET"), + c ? c->file : 0, "0")); - /* resolve command into executable path */ - if (!g_strict || !sandboxed) + unsandboxed = parse_bool (get_target_variable + (STRING_SIZE_TUPLE(".UNSANDBOXED"), + c ? c->file : 0, "0")); + + if (c) + { + sandboxed = !unsandboxed; + strict = parse_bool (get_target_variable + (STRING_SIZE_TUPLE(".STRICT"), + c->file, "0")); + } + else + { + sandboxed = false; + strict = false; + } + + if (!unsandboxed) + { + promises = emptytonull (get_target_variable + (STRING_SIZE_TUPLE(".PLEDGE"), + c ? c->file : 0, 0)); + if (promises) + promises = xstrdup (promises); + if (ParsePromises (promises, &ipromises)) + { + OSS (error, NILF, "%s: invalid .PLEDGE string: %s", + argv[0], strerror (errno)); + _Exit (127); + } + } + else + { + promises = NULL; + ipromises = 0; + } + + DB (DB_JOBS, + (_("Executing %s for %s%s%s%s\n"), + argv[0], c ? c->file->name : "$(shell)", + sandboxed ? " with sandboxing" : " without sandboxing", + strict ? " in .STRICT mode" : "", + internet ? " with internet access" : "")); + + /* [jart] Set cpu seconds quota. */ + if ((s = get_target_variable (STRING_SIZE_TUPLE(".CPU"), + c ? c->file : 0, 0))) + { + int secs; + secs = atoi (s); + DB (DB_JOBS, (_("Setting cpu limit of %d seconds\n"), secs)); + set_cpu_limit (secs); + } + + /* [jart] Set virtual memory quota. */ + if ((s = get_target_variable (STRING_SIZE_TUPLE(".MEMORY"), + c ? c->file : 0, 0))) + { + long bytes; + char buf[16]; + if (!strchr (s, '%')) + bytes = sizetol (s, 1024); + else + bytes = strtod (s, 0) / 100. * g_sysinfo.totalram; + DB (DB_JOBS, (_("Setting virtual memory limit of %s\n"), + (FormatMemorySize (buf, bytes, 1024), buf))); + set_mem_limit (bytes); + } + + /* [jart] Set file size limit. */ + if ((s = get_target_variable (STRING_SIZE_TUPLE(".FSIZE"), + c ? c->file : 0, 0))) + { + long bytes; + char buf[16]; + bytes = sizetol (s, 1000); + DB (DB_JOBS, (_("Setting file size limit of %s\n"), + (FormatMemorySize (buf, bytes, 1000), buf))); + set_fsz_limit (bytes); + } + + /* [jart] Set process limit. */ + if ((s = get_target_variable (STRING_SIZE_TUPLE(".NPROC"), + c ? c->file : 0, 0))) + { + int procs; + if ((procs = atoi (s)) > 0) + { + DB (DB_JOBS, (_("Setting process limit to %d + %d preexisting\n"), + procs, g_sysinfo.procs)); + set_pro_limit (procs + g_sysinfo.procs); + } + } + + /* [jart] Prevent builds from talking to the Internet. */ + if (internet) + DB (DB_JOBS, (_("Allowing Internet access\n"))); + else if (!(~ipromises & (1ul << PROMISE_INET)) && + !(~ipromises & (1ul << PROMISE_DNS))) + DB (DB_JOBS, (_("Internet access will be blocked by pledge\n"))); + else + { + e = errno; + if (!nointernet()) + DB (DB_JOBS, (_("Blocked Internet access with seccomp ptrace\n"))); + else + { + if (errno = EPERM) + { + errno = e; + DB (DB_JOBS, (_("Can't block Internet if already traced\n"))); + } + else if (errno == ENOSYS) + { + errno = e; + DB (DB_JOBS, (_("Need SECCOMP ptrace() to block Internet\n"))); + } + else + { + OSS (error, NILF, "%s: failed to block internet access: %s", + argv[0], strerror (errno)); + _Exit (127); + } + } + } + + /* [jart] Resolve command into executable path. */ + if (!strict || !sandboxed) { if ((s = commandv (argv[0], pathbuf, sizeof (pathbuf)))) argv[0] = s; @@ -1841,15 +2062,13 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) } } - /* [jart] sandbox command based on prerequisites */ + /* [jart] Sandbox build rule commands based on prerequisites. */ if (c) { errno = 0; if (sandboxed) { - DB (DB_JOBS, (_("Sandboxing %s\n"), c->file->name)); - - if (!g_strict && argv[0][0] == '/' && IsDynamicExecutable (argv[0])) + if (!strict && argv[0][0] == '/' && IsDynamicExecutable (argv[0])) { /* * weaken sandbox if user is using dynamic shared lolbjects @@ -1883,6 +2102,8 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) * only if the ape loader exists on a well-known path. */ e = errno; + DB (DB_JOBS, (_("Unveiling %s with permissions %s\n"), + "/usr/bin/ape", "rx")); if (unveil ("/usr/bin/ape", "rx") == -1) { char *s, *t; @@ -1902,15 +2123,15 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) } } - /* unveil executable */ + /* Unveil executable. */ RETURN_ON_ERROR (Unveil (argv[0], "rx")); - /* unveil temporary directory */ + /* Unveil temporary directory. */ if (c->tmpdir) RETURN_ON_ERROR (Unveil (c->tmpdir, "rwcx")); - /* unveil lazy mode files */ - if (!g_strict) + /* Unveil lazy mode files. */ + if (!strict) { RETURN_ON_ERROR (Unveil ("/tmp", "rwc")); RETURN_ON_ERROR (Unveil ("/dev/zero", "r")); @@ -1922,6 +2143,50 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv) RETURN_ON_ERROR (Unveil ("/etc/hosts", "r")); } + /* Unveil .PLEDGE = tmppath. */ + if (!strict && promises && (~ipromises & (1ul << PROMISE_TMPPATH))) + RETURN_ON_ERROR (Unveil ("/tmp", "rwc")); + + /* Unveil .PLEDGE = vminfo. */ + if (promises && (~ipromises & (1ul << PROMISE_VMINFO))) + { + RETURN_ON_ERROR (Unveil ("/proc/stat", "r")); + RETURN_ON_ERROR (Unveil ("/proc/meminfo", "r")); + RETURN_ON_ERROR (Unveil ("/proc/cpuinfo", "r")); + RETURN_ON_ERROR (Unveil ("/proc/diskstats", "r")); + RETURN_ON_ERROR (Unveil ("/proc/self/maps", "r")); + RETURN_ON_ERROR (Unveil ("/sys/devices/system/cpu", "r")); + } + + /* Unveil .PLEDGE = tty. */ + if (promises && (~ipromises & (1ul << PROMISE_TTY))) + { + RETURN_ON_ERROR (Unveil (ttyname(0), "rw")); + RETURN_ON_ERROR (Unveil ("/dev/tty", "rw")); + RETURN_ON_ERROR (Unveil ("/dev/console", "rw")); + RETURN_ON_ERROR (Unveil ("/etc/terminfo", "r")); + RETURN_ON_ERROR (Unveil ("/usr/lib/terminfo", "r")); + RETURN_ON_ERROR (Unveil ("/usr/share/terminfo", "r")); + } + + /* Unveil .PLEDGE = dns. */ + if (promises && (~ipromises & (1ul << PROMISE_DNS))) + { + RETURN_ON_ERROR (Unveil ("/etc/hosts", "r")); + RETURN_ON_ERROR (Unveil ("/etc/hostname", "r")); + RETURN_ON_ERROR (Unveil ("/etc/services", "r")); + RETURN_ON_ERROR (Unveil ("/etc/protocols", "r")); + RETURN_ON_ERROR (Unveil ("/etc/resolv.conf", "r")); + } + + /* Unveil .PLEDGE = inet. */ + if (promises && (~ipromises & (1ul << PROMISE_INET))) + RETURN_ON_ERROR (Unveil ("/etc/ssl/certs/ca-certificates.crt", "r")); + + /* Unveil .PLEDGE = rpath. */ + if (promises && (~ipromises & (1ul << PROMISE_INET))) + RETURN_ON_ERROR (Unveil ("/proc/filesystems", "r")); + /* * unveils target output file * @@ -2017,6 +2282,20 @@ exec_command (char **argv, char **envp) /* Be the user, permanently. */ child_access (); + /* Restrict system calls. */ + if (promises) + { + __pledge_mode = PLEDGE_PENALTY_RETURN_EPERM; + DB (DB_JOBS, (_("Pledging %s\n"), promises)); + promises = xstrcat (promises, " prot_exec exec"); + if (pledge (promises, promises)) + { + OSS (error, NILF, "pledge(%s) failed: %s", + promises, strerror (errno)); + _Exit (127); + } + } + /* Run the program. */ environ = envp; execv (argv[0], argv); diff --git a/third_party/make/job.h b/third_party/make/job.h index 1219258a6..f52846e25 100644 --- a/third_party/make/job.h +++ b/third_party/make/job.h @@ -71,7 +71,7 @@ void start_waiting_jobs (void); char **construct_command_argv (char *line, char **restp, struct file *file, int cmd_flags, char** batch_file); -pid_t child_execute_job (struct childbase *child, int good_stdin, char **argv); +pid_t child_execute_job (struct childbase *, int, char **, bool); #ifdef _AMIGA void exec_command (char **argv) NORETURN; diff --git a/third_party/make/main.c b/third_party/make/main.c index 5071c7c4a..388dbf9ab 100644 --- a/third_party/make/main.c +++ b/third_party/make/main.c @@ -14,40 +14,36 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* clang-format off */ #include "third_party/make/makeint.inc" -#include "third_party/make/os.h" +/**/ #include "third_party/make/filedef.h" +#include "third_party/make/os.h" +/**/ #include "third_party/make/dep.h" -#include "third_party/make/variable.h" #include "third_party/make/job.h" +#include "third_party/make/variable.h" +/**/ #include "third_party/make/commands.h" -#include "third_party/make/rule.h" #include "third_party/make/debug.h" -#include "libc/runtime/stack.h" -#include "libc/limits.h" -#include "libc/sysv/consts/sig.h" -#include "libc/log/log.h" -#include "libc/log/log.h" -#include "libc/log/log.h" -#include "libc/sock/sock.h" -#include "libc/dce.h" -#include "libc/calls/syscall_support-sysv.internal.h" -#include "libc/calls/struct/seccomp.h" -#include "libc/calls/struct/bpf.h" -#include "libc/sysv/consts/audit.h" -#include "libc/calls/struct/seccomp.h" -#include "libc/sysv/consts/pr.h" +#include "third_party/make/rule.h" +/**/ #include "libc/calls/calls.h" -#include "libc/macros.internal.h" -#include "libc/stdio/stdio.h" +#include "libc/calls/struct/bpf.h" #include "libc/calls/struct/filter.h" +#include "libc/calls/struct/seccomp.h" +#include "libc/calls/syscall_support-sysv.internal.h" +#include "libc/dce.h" +#include "libc/limits.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" -#include "libc/runtime/runtime.h" -#include "libc/runtime/runtime.h" -#include "libc/log/log.h" -#include "libc/log/log.h" +#include "libc/runtime/stack.h" +#include "libc/sock/sock.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/audit.h" +#include "libc/sysv/consts/pr.h" +#include "libc/sysv/consts/sig.h" #include "third_party/make/getopt.h" +// clang-format off STATIC_STACK_SIZE(0x200000); // 2mb stack @@ -985,10 +981,6 @@ main (int argc, char **argv, char **envp) unsigned int syncing = 0; int argv_slots; - // block internet access - if (!getenv("MAKE_RESTARTS")) - nointernet (); - /* Useful for attaching debuggers, etc. */ SPIN ("main-entry"); @@ -2982,7 +2974,7 @@ print_version (void) /* Do it only once. */ return; - printf ("%sLandlock Make 1.1.1 (GNU Make %s)\n", precede, version_string); + printf ("%sLandlock Make 1.2 (GNU Make %s)\n", precede, version_string); if (!remote_description || *remote_description == '\0') printf (_("%sBuilt for %s\n"), precede, make_host); diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index bd59ac2a7..545ab8208 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -254,7 +254,9 @@ static int OpMunmap(struct Machine *m, int64_t virt, uint64_t size) { static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot, int flags, int fd, int64_t offset) { + int e; void *tmp; + ssize_t rc; uint64_t key; VERBOSEF("MMAP%s %012lx %,ld %#x %#x %d %#lx", GetSimulated(), virt, size, prot, flags, fd, offset); @@ -277,7 +279,13 @@ static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot, if (fd != -1 && !(flags & MAP_ANONYMOUS)) { /* TODO: lazy file mappings */ CHECK_NOTNULL((tmp = malloc(size))); - CHECK_EQ(size, pread(fd, tmp, size, offset)); + for (e = errno;;) { + rc = pread(fd, tmp, size, offset); + if (rc != -1) break; + CHECK_EQ(EINTR, errno); + errno = e; + } + CHECK_EQ(size, rc); VirtualRecvWrite(m, virt, tmp, size); free(tmp); } diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 294a5f6b1..7587b960c 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -7104,7 +7104,7 @@ static void HandleShutdown(void) { } // this function coroutines with linenoise -static int EventLoop(int ms) { +int EventLoop(int ms) { struct timespec t; DEBUGF("(repl) event loop"); while (!terminated) { diff --git a/tool/viz/memplan.c b/tool/viz/memplan.c index 904606431..daeb5315e 100644 --- a/tool/viz/memplan.c +++ b/tool/viz/memplan.c @@ -7,9 +7,9 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/intrin/bits.h" #include "libc/calls/internal.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/bits.h" #include "libc/macros.internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/stdio/stdio.h" @@ -30,7 +30,7 @@ uint64_t last; void plan2(uint64_t addr, uint64_t end, const char *name) { char sz[32]; - FormatMemorySize(sz, end-addr+1); + FormatMemorySize(sz, end-addr+1, 1024); printf("%08x-%08x %-6s %s\n", addr>>16, end>>16, sz, name); }