From 8dfc7699a6ffd4875ba4ead8215ed3a373b618f1 Mon Sep 17 00:00:00 2001 From: Lorenzo Cavallaro Date: Tue, 30 Mar 2010 09:36:46 +0000 Subject: [PATCH] cdecl calling convention requires to push arguments on the stack in a reverse order to easily support variadic arguments. Thus, instead of using the proper stdarg.h macros (that nowadays are compiler-dependent), it may be tempting to directly take the address of the last argument and considering it as the start of an array. This is a shortcut that avoid looping to get all the arguments as the CPU already pushed them on the stack before the call to the function. Unfortunately, such an assumption is strictly compiler-dependent and compilers are free to move the last argument on the stack, as a local variable, and return the address of the location where the argument was stored, if asked for. This will break things as the rest of the array's argument are stored elsewhere (typically, a couple of words above the location where the argument was stored). This patch fixes the issue by allowing ACK to take the shortcut and enabling gcc/llvm-gcc to follow the right way. --- include/minix/compiler-ack.h | 11 ++++++ include/minix/compiler.h | 28 ++++++++++++- include/stdarg.h | 18 ++------- lib/libc/posix/_execl.c | 77 +++++++++++++++++++++++++++++++++--- lib/libc/posix/_execle.c | 72 +++++++++++++++++++++++++++++---- lib/libc/posix/_execlp.c | 65 +++++++++++++++++++++++++++++- lib/libc/posix/_execve.c | 13 +++--- lib/libc/posix/_execvp.c | 4 +- 8 files changed, 249 insertions(+), 39 deletions(-) diff --git a/include/minix/compiler-ack.h b/include/minix/compiler-ack.h index a5ab4db10..cb6e035a5 100644 --- a/include/minix/compiler-ack.h +++ b/include/minix/compiler-ack.h @@ -6,4 +6,15 @@ /* ACK expects the caller to pop the hidden pointer on struct return. */ #define BYTES_TO_POP_ON_STRUCT_RETURN +/* + * ACK doesn't move the last argument of a variadic arguments function + * anywhere, once it's on the stack as a function parameter. Thus, it is + * possible to make strong assumption on the immutability of the stack + * layout and use the address of that argument as the start of an array. + * + * If you're curious, just look at lib/libc/posix/_execl*.c ;-) + */ + +#define FUNC_ARGS_ARRAY 1 + #endif /* _MINIX_COMPILER_ACK_H */ diff --git a/include/minix/compiler.h b/include/minix/compiler.h index 25732d156..75b5d6350 100644 --- a/include/minix/compiler.h +++ b/include/minix/compiler.h @@ -4,7 +4,7 @@ #define _MINIX_COMPILER_H /*===========================================================================* - * Compiler overrides * + * Compiler overrides * *===========================================================================*/ /* ACK */ #ifdef __ACK__ @@ -12,7 +12,7 @@ #endif /*===========================================================================* - * Default values * + * Default values * *===========================================================================*/ /* * cdecl calling convention expects the callee to pop the hidden pointer on @@ -22,4 +22,28 @@ #define BYTES_TO_POP_ON_STRUCT_RETURN $4 #endif +/* + * cdecl calling convention requires to push arguments on the stack in a + * reverse order to easily support variadic arguments. Thus, instead of + * using the proper stdarg.h macros (that nowadays are + * compiler-dependant), it may be tempting to directly take the address of + * the last argument and considering it as the start of an array. This is + * a shortcut that avoid looping to get all the arguments as the CPU + * already pushed them on the stack before the call to the function. + * + * Unfortunately, such an assumption is strictly compiler-dependant and + * compilers are free to move the last argument on the stack, as a local + * variable, and return the address of the location where the argument was + * stored, if asked for. This will break things as the rest of the array's + * argument are stored elsewhere (typically, a couple of words above the + * location where the argument was stored). + * + * Conclusion: if unsure on what the compiler may do, do not make any + * assumption and use the right (typically compiler-dependant) macros. + */ + +#ifndef FUNC_ARGS_ARRAY +#define FUNC_ARGS_ARRAY 0 +#endif + #endif /* _MINIX_COMPILER_H */ diff --git a/include/stdarg.h b/include/stdarg.h index c5ea6b9f4..40a4298f8 100644 --- a/include/stdarg.h +++ b/include/stdarg.h @@ -53,20 +53,10 @@ void va_end (va_list); /* Defined in gnulib */ #else /* __GNUC__ >= 2 */ -#ifndef __sparc__ -#define va_start(AP, LASTARG) \ - (AP = ((char *) __builtin_next_arg ())) -#else -#define va_start(AP, LASTARG) \ - (__builtin_saveregs (), AP = ((char *) __builtin_next_arg ())) -#endif - -void va_end (va_list); /* Defined in libgcc.a */ -#define va_end(AP) - -#define va_arg(AP, TYPE) \ - (AP = ((char *) (AP)) += __va_rounded_size (TYPE), \ - *((TYPE *) ((char *) (AP) - __va_rounded_size (TYPE)))) +#define va_start(ap, last) __builtin_va_start((ap), (last)) +#define va_arg(ap, type) __builtin_va_arg((ap), type) +#define va_end(ap) __builtin_va_end(ap) +#define va_copy(dest, src) __builtin_va_copy((dest), (src)) #endif /* __GNUC__ >= 2 */ diff --git a/lib/libc/posix/_execl.c b/lib/libc/posix/_execl.c index 4592d0731..9337e59ae 100644 --- a/lib/libc/posix/_execl.c +++ b/lib/libc/posix/_execl.c @@ -1,18 +1,85 @@ -/* execl() - execute Author: Kees J. Bot - * 21 Jan 1994 +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + #define execl _execl #define execve _execve #include +#include +#include +#include +#include +#include +#include +#include extern char * const **_penviron; /* The default environment. */ -int execl(const char *path, const char *arg1, ...) +int +execl(const char *path, const char *arg, ...) /* execl("/bin/sh", "sh", "-c", "example", (char *) 0); */ { - /* Assumption: The C-implementation for this machine pushes +#if FUNC_ARGS_ARRAY + /* + * Assumption: The C-implementation for this machine pushes * function arguments downwards on the stack making a perfect * argument array. Luckily this is almost always so. + * + * execl() - execute Author: Kees J. Bot, 21 Jan 1994 + */ + return execve(path, (char * const *) &arg, *_penviron); +#else + /* + * Indeed, GCC (4.4.1) behaves similarly to ACK. Nonetheless, let's + * stay the safe side. LLVM (llvm-gcc) doesn't make the aforementioned + * assumption. */ - return execve(path, (char * const *) &arg1, *_penviron); + va_list ap; + char **argv; + int i; + + va_start(ap, arg); + for (i = 2; va_arg(ap, char *) != NULL; i++) + continue; + va_end(ap); + + if ((argv = alloca(i * sizeof (char *))) == NULL) { + errno = ENOMEM; + return -1; + } + + va_start(ap, arg); + argv[0] = __UNCONST(arg); + for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++) + continue; + va_end(ap); + + return execve(path, (char * const *) argv, *_penviron); +#endif } + diff --git a/lib/libc/posix/_execle.c b/lib/libc/posix/_execle.c index 10a814dcc..bb9fe1987 100644 --- a/lib/libc/posix/_execle.c +++ b/lib/libc/posix/_execle.c @@ -1,25 +1,83 @@ -/* execle() - execute with a custom environment Author: Kees J. Bot - * 21 Jan 1994 +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ -#define nil 0 + #define execle _execle #define execve _execve #include +#include +#include #include +#include +#include +#include +#include int execle(const char *path, const char *arg1, ...) /* execle("/bin/sh", "sh", "-c", "example", (char *) 0, my_env_array); */ { - char * const * envp; - va_list ap; - + char * const * envp; + va_list ap; +#if FUNC_ARGS_ARRAY + /* + * execle() - execute with a custom environment + * Author: Kees J. Bot, 21 Jan 1994 + */ va_start(ap, arg1); /* Find the end of the argument array. */ - if (arg1 != nil) while (va_arg(ap, const char *) != nil) {} + if (arg1 != NULL) while (va_arg(ap, const char *) != NULL) {} envp = va_arg(ap, char * const *); va_end(ap); return execve(path, (char * const *) &arg1, envp); +#else + char **argv; + int i; + + va_start(ap, arg1); + for (i = 2; va_arg(ap, char *) != NULL; i++) + continue; + va_end(ap); + + if ((argv = alloca(i * sizeof (char *))) == NULL) { + errno = ENOMEM; + return -1; + } + + va_start(ap, arg1); + argv[0] = __UNCONST(arg1); + for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++) + continue; + envp = va_arg(ap, char **); + va_end(ap); + + return execve(path, (char * const *)argv, envp); +#endif } diff --git a/lib/libc/posix/_execlp.c b/lib/libc/posix/_execlp.c index 73dfdc389..4d3daefd0 100644 --- a/lib/libc/posix/_execlp.c +++ b/lib/libc/posix/_execlp.c @@ -1,12 +1,73 @@ -/* execlp() - execute with PATH search Author: Kees J. Bot - * 22 Jan 1994 +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + #define execlp _execlp #define execvp _execvp #include +#include +#include +#include +#include +#include +#include +#include int execlp(const char *file, const char *arg1, ...) /* execlp("sh", "sh", "-c", "example", (char *) 0); */ { +#if FUNC_ARGS_ARRAY + /* + * execlp() - execute with PATH search + * Author: Kees J. Bot, 22 Jan 1994 + */ return execvp(file, (char * const *) &arg1); +#else + va_list ap; + char **argv; + int i; + + va_start(ap, arg1); + for (i = 2; va_arg(ap, char *) != NULL; i++) + continue; + va_end(ap); + + if ((argv = alloca(i * sizeof (char *))) == NULL) { + errno = ENOMEM; + return -1; + } + + va_start(ap, arg1); + argv[0] = __UNCONST(arg1); + for (i = 1; (argv[i] = va_arg(ap, char *)) != NULL; i++) + continue; + va_end(ap); + + return execvp(file, (char * const *)argv); +#endif } diff --git a/lib/libc/posix/_execve.c b/lib/libc/posix/_execve.c index 3a5b6c106..4b2ec05ff 100644 --- a/lib/libc/posix/_execve.c +++ b/lib/libc/posix/_execve.c @@ -4,7 +4,6 @@ #define _MINIX_SOURCE -#define nil 0 #define execve _execve #define sbrk _sbrk #include @@ -37,7 +36,7 @@ int execve(const char *path, char * const *argv, char * const *envp) string_off= 0; /* Offset to start of the strings. */ argc= 0; /* Argument count. */ - for (ap= argv; *ap != nil; ap++) { + for (ap= argv; *ap != NULL; ap++) { n = sizeof(*ap) + strlen(*ap) + 1; frame_size+= n; if (frame_size < n) ov= 1; @@ -45,7 +44,7 @@ int execve(const char *path, char * const *argv, char * const *envp) argc++; } - for (ep= envp; *ep != nil; ep++) { + for (ep= envp; *ep != NULL; ep++) { n = sizeof(*ep) + strlen(*ep) + 1; frame_size+= n; if (frame_size < n) ov= 1; @@ -77,22 +76,22 @@ int execve(const char *path, char * const *argv, char * const *envp) sp = frame + string_off; /* Load the argument vector and strings. */ - for (ap= argv; *ap != nil; ap++) { + for (ap= argv; *ap != NULL; ap++) { *vp++= (char *) (sp - frame); n= strlen(*ap) + 1; memcpy(sp, *ap, n); sp+= n; } - *vp++= nil; + *vp++= NULL; /* Load the environment vector and strings. */ - for (ep= envp; *ep != nil; ep++) { + for (ep= envp; *ep != NULL; ep++) { *vp++= (char *) (sp - frame); n= strlen(*ep) + 1; memcpy(sp, *ep, n); sp+= n; } - *vp++= nil; + *vp++= NULL; /* Padding. */ while (sp < frame + frame_size) *sp++= 0; diff --git a/lib/libc/posix/_execvp.c b/lib/libc/posix/_execvp.c index 14b768b1d..fecb08088 100644 --- a/lib/libc/posix/_execvp.c +++ b/lib/libc/posix/_execvp.c @@ -5,7 +5,6 @@ #define _MINIX_SOURCE -#define nil 0 #define execve _execve #define execvp _execvp #define sbrk _sbrk @@ -15,6 +14,7 @@ #include #include #include +#include extern char * const **_penviron; /* The default environment. */ @@ -33,7 +33,7 @@ int execvp(const char *file, char * const *argv) size_t full_size; int err= ENOENT; /* Error return on failure. */ - if (strchr(file, '/') != nil || (path= getenv("PATH")) == nil) + if (strchr(file, '/') != NULL || (path= getenv("PATH")) == NULL) path= ""; /* Compute the maximum length the full name may have, and align. */ -- 2.44.0