From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server-vie001.gnuweeb.org X-Spam-Level: ** X-Spam-Status: No, score=2.5 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,RDNS_NONE,SPF_PASS, URIBL_DBL_BLOCKED_OPENDNS autolearn=no autolearn_force=no version=3.4.6 Authentication-Results: server-vie001.gnuweeb.org; dkim=pass (2048-bit key; unprotected) header.d=openresty.com header.i=@openresty.com header.a=rsa-sha256 header.s=google header.b=Kgrh4pDV; dkim-atps=neutral Received: by server-vie001.gnuweeb.org (Postfix, from userid 1000) id C6F582DEF48F; Sun, 26 Jul 2026 10:13:36 +0000 (UTC) Authentication-Results: server-vie001.gnuweeb.org; dmarc=none (p=none dis=none) header.from=openresty.com Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::62c; helo=mail-pl1-x62c.google.com; envelope-from=ammarfaizi2@openresty.com; receiver= Received: from mail-pl1-x62c.google.com (unknown [IPv6:2607:f8b0:4864:20::62c]) by server-vie001.gnuweeb.org (Postfix) with ESMTPS id 78E352DEF45A for ; Sun, 26 Jul 2026 10:13:25 +0000 (UTC) Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-2ceaf8a1265so21996415ad.2 for ; Sun, 26 Jul 2026 03:13:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openresty.com; s=google; t=1785060799; x=1785665599; darn=gnuweeb.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=6vm5tLIDnBxoRlmuntR1gC/2V5EsdXXeIFZY5yD6sLc=; b=Kgrh4pDVAdGrhZjkfAZNWzHhGkvb/eeaW3K8BbHnCXtpyTgn0LlEGOgHyvWTdcj0p/ QaLDBSau+XMIOlaaqpVNw0gyB6+T60a31bjVbkcIA3OSkKbLibDl7jO6f3UdjhGf9u+r T9m8J0yI//UGIh++3nF3uuiIjHUhdNExH2aZrgVurGc+5ZiSD+LHIvQJ5FYnYe85DDLG Sf+1zNs66ooF4tRJcabrNq+H2a5Gsedvn40Xa+vy1Rvt2wAePsmy8hJb6oiHXBsFXIzk P3R3NTA+RaRM05dY/5uWipyxqRNSXfgzIgGmhFZkPM8nJGw/hc6agQAb6fKb8h1bm/s+ QlTQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785060799; x=1785665599; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=6vm5tLIDnBxoRlmuntR1gC/2V5EsdXXeIFZY5yD6sLc=; b=e3uHjJFV0A2HBfNSjnYnobul00Ge6FYIJh7ImiWeT7UbiKpoxXZ9bS9zb+PA/LlX9g 4qly4Gt1ZbWzD7zYQLfhgApdS5ypm6FCBa6g7G69hRL6cH1k6XBKtN2IXIpe++xzuNtu cmnUwk5vZrgjiorrkA551MDFDTnPEOh/tXl85yfPjXw0v0kta28iQID08+bKGV5UihHU u2GD71ejGoGL4puq7BbJrvGEQZhP2J+0ZhBmhM7JqL3qTSYvXAQiZOfBLM7g/yUk5rAh BrW/kM3CHc1z0VePHeD1OcC6SC+axchkr4s0ey6xLRodjeEGTBm6tEPIrgdbK1ZQ9uFE dsxw== X-Forwarded-Encrypted: i=1; AHgh+RqkP72mo3gNJ5sjb2QgRTgTqzYMfExEjk66DlQcDKx+mn45qhKOIIBcNShsBgdx0xsj3I9o@gnuweeb.org X-Gm-Message-State: AOJu0YyoL+/aPsaQdWlGw4vn+vhjFJyTx/2K0y0FIf+ky8Faz094eL7M vlOZHOLcuChRL4mY6aFoyFj3k0TmPDAgirzp3QXOeUNEQ4CNsPVvtDmHymQo4IbfFW4= X-Gm-Gg: AR+sD12VBDNNawtQfl56Tr9xgSErBeQzmePckcbq9smVbUU/orZjgknW0e40VFHLWwJ zuONIf5onnT12luanfkhpChQ5Fqr1HICv7QSpvxXtARLecmAKhhCJ/APrDaUp3wrrOjKIVrOn+h 2wXBm+4ZiKV5a77+feOP+ivB7YYBzFc4grQGT4r/WnDwXgHgYZhDO8BN+A1Or7hQgUj90d9uoHi sty3f9vQ0q//TFOeLuquSOxgau2kaCyiRlfaRd5McpXaAVLKyiuGMDrqq6WJLtP6z7kq183C/jC M9c6KxAwVKMVN70xgUjRvLfpfDwiYcKzib7wgGWrLYnjiypzgLdQvI5bldx54YUFSELljKF6SVn 7dAGV1HIGssUtU3nQPWrI2qofej8dTtgYnhlbiev1FbtuKw10Fy9M22HhENZu8exeterO/w6VHm GPwi6rZrtmRSHigdvpLe4Kpte7 X-Received: by 2002:a05:6a20:c79a:b0:3c3:7a0a:18a7 with SMTP id adf61e73a8af0-3c67e13a7a8mr4326893637.55.1785060799033; Sun, 26 Jul 2026 03:13:19 -0700 (PDT) Received: from integral2.. ([2402:8780:1329:2779:9db7:a8ec:2e1c:53e1]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-314bc59e9fesm17243981eec.27.2026.07.26.03.13.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 03:13:18 -0700 (PDT) From: Ammar Faizi To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Cc: Ammar Faizi , Linux Kernel Mailing List , Linux Kselftest Mailing List , LLVM Mailing List , Yichun Zhang , Alviro Iskandar Setiawan , Shuah Khan , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , gwml@gnuweeb.org Subject: [PATCH 1/4] tools/nolibc: evaluate syscall() arguments before the arch macros Date: Sun, 26 Jul 2026 17:13:02 +0700 Message-Id: <20260726101306.3772237-2-ammarfaizi2@openresty.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260726101306.3772237-1-ammarfaizi2@openresty.com> References: <20260726101306.3772237-1-ammarfaizi2@openresty.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Per the GCC docs, local `register` variables aren't guaranteed to survive a function call, and you shouldn't put code between the register assignment and the `asm` block: https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html The __nolibc_syscallN() macros put their arguments into local register variables. syscall() drops caller expressions straight into those macros, so this: static char msg[] = "Hello, World!\n"; syscall(__NR_write, 1, msg, strlen(msg)); runs the wrong syscall. On x86-64, GCC doesn't reload %rax, %rdi, and %rsi after the strlen() call, so only %rdx ends up correct. Fix it by evaluating the arguments into temporaries first, before they reach the arch macros. Before this patch (bug): ``` 0000000000401000
: pushq %rcx movl $0x403000,%edi callq 401153 # strlen() clobbers %rax, %rdi, %rsi. movq %rax,%rdx # BUG: %rax, %rdi, %rsi are wrong. syscall # Only %rdx is correct. [...] popq %rdx retq ``` After this patch (fixed): ``` 0000000000401000
: pushq %rcx movl $0x403000,%edi callq 40116c movl $0x1,%edi # %rdi = 1 (stdout) movl $0x403000,%esi # %rsi = msg movq %rax,%rdx # %rdx = strlen(msg) movl $0x1,%eax # %rax = __NR_write syscall [...] popq %rdx retq ``` Reproduced with gcc on i386 and x86-64 at -O0, -O1, -O2, -O3 and -Os. Found this bug after a chat with Alviro. I also attempted to report a similar problem to the GCC bugzilla: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126290 The solution from GNU developers is: use hard register constraints (introduced in GCC 16) to reliably force asm operands into specific registers, though Clang hasn't merged its version yet: https://gcc.gnu.org/onlinedocs/gcc-16.1.0/gcc/Hard-Register-Constraints.html https://github.com/llvm/llvm-project/pull/85846 Once hard register constraints are widely available (in the future), a reliable inline asm for a syscall may look like this: ``` #define syscall6(N, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) ({ \ long long ret = (N); \ __asm__ volatile( \ "syscall" \ : "+{rax}"(ret) \ : "{rdi}"(ARG1), \ "{rsi}"(ARG2), \ "{rdx}"(ARG3), \ "{r10}"(ARG4), \ "{r8}"(ARG5), \ "{r9}"(ARG6) \ : "rcx", "r11", "memory"); \ (ret); \ }) ``` Cc: Yichun Zhang Fixes: 53fcfafa8c5c ("tools/nolibc/unistd: add syscall()") Reported-by: Alviro Iskandar Setiawan Signed-off-by: Ammar Faizi --- tools/include/nolibc/sys/syscall.h | 72 +++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/tools/include/nolibc/sys/syscall.h b/tools/include/nolibc/sys/syscall.h index 7f06314fcf0d..b91b82846fe4 100644 --- a/tools/include/nolibc/sys/syscall.h +++ b/tools/include/nolibc/sys/syscall.h @@ -10,9 +10,79 @@ #ifndef _NOLIBC_SYS_SYSCALL_H #define _NOLIBC_SYS_SYSCALL_H +/* + * The __nolibc_syscallN() macros assign their arguments to local register + * variables. A compiler only has to keep such a variable in its register + * right before the asm statement it feeds, so an argument expression which + * contains a function call gets evaluated once the earlier arguments already + * sit in their registers, and the call then clobbers them. + * + * Caller-supplied expressions enter here, so bind them to temporaries first + * and only hand plain variables over. __auto_type preserves the original + * type, so nothing gets truncated on the way. + */ +#define __nolibc_syscall_eval0(_n) \ +({ \ + __auto_type __sc_n = (_n); \ + __nolibc_syscall0(__sc_n); \ +}) +#define __nolibc_syscall_eval1(_n, _a1) \ +({ \ + __auto_type __sc_n = (_n); \ + __auto_type __sc_a1 = (_a1); \ + __nolibc_syscall1(__sc_n, __sc_a1); \ +}) +#define __nolibc_syscall_eval2(_n, _a1, _a2) \ +({ \ + __auto_type __sc_n = (_n); \ + __auto_type __sc_a1 = (_a1); \ + __auto_type __sc_a2 = (_a2); \ + __nolibc_syscall2(__sc_n, __sc_a1, __sc_a2); \ +}) +#define __nolibc_syscall_eval3(_n, _a1, _a2, _a3) \ +({ \ + __auto_type __sc_n = (_n); \ + __auto_type __sc_a1 = (_a1); \ + __auto_type __sc_a2 = (_a2); \ + __auto_type __sc_a3 = (_a3); \ + __nolibc_syscall3(__sc_n, __sc_a1, __sc_a2, __sc_a3); \ +}) +#define __nolibc_syscall_eval4(_n, _a1, _a2, _a3, _a4) \ +({ \ + __auto_type __sc_n = (_n); \ + __auto_type __sc_a1 = (_a1); \ + __auto_type __sc_a2 = (_a2); \ + __auto_type __sc_a3 = (_a3); \ + __auto_type __sc_a4 = (_a4); \ + __nolibc_syscall4(__sc_n, __sc_a1, __sc_a2, __sc_a3, __sc_a4); \ +}) +#define __nolibc_syscall_eval5(_n, _a1, _a2, _a3, _a4, _a5) \ +({ \ + __auto_type __sc_n = (_n); \ + __auto_type __sc_a1 = (_a1); \ + __auto_type __sc_a2 = (_a2); \ + __auto_type __sc_a3 = (_a3); \ + __auto_type __sc_a4 = (_a4); \ + __auto_type __sc_a5 = (_a5); \ + __nolibc_syscall5(__sc_n, __sc_a1, __sc_a2, __sc_a3, __sc_a4, \ + __sc_a5); \ +}) +#define __nolibc_syscall_eval6(_n, _a1, _a2, _a3, _a4, _a5, _a6) \ +({ \ + __auto_type __sc_n = (_n); \ + __auto_type __sc_a1 = (_a1); \ + __auto_type __sc_a2 = (_a2); \ + __auto_type __sc_a3 = (_a3); \ + __auto_type __sc_a4 = (_a4); \ + __auto_type __sc_a5 = (_a5); \ + __auto_type __sc_a6 = (_a6); \ + __nolibc_syscall6(__sc_n, __sc_a1, __sc_a2, __sc_a3, __sc_a4, \ + __sc_a5, __sc_a6); \ +}) + #define ___nolibc_syscall_narg(_0, _1, _2, _3, _4, _5, _6, N, ...) N #define __nolibc_syscall_narg(...) ___nolibc_syscall_narg(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0) -#define __nolibc_syscall(N, ...) __nolibc_syscall##N(__VA_ARGS__) +#define __nolibc_syscall(N, ...) __nolibc_syscall_eval##N(__VA_ARGS__) #define __nolibc_syscall_n(N, ...) __nolibc_syscall(N, __VA_ARGS__) #define _syscall(...) __nolibc_syscall_n(__nolibc_syscall_narg(__VA_ARGS__), ##__VA_ARGS__) #define syscall(...) __sysret(_syscall(__VA_ARGS__)) -- Ammar Faizi