From 9220b96d3c8ec555212d8aee0f81f1c898c75190 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Sun, 1 Oct 2023 19:06:33 +0000 Subject: [PATCH] Fix [7371b6270b]: AddressSanitizer use-after-return detection breaks NRE tests, coroutines. (patch by chrstphrchvz, with some minor modifications) --- generic/tclBasic.c | 39 +++++++++++++++++++++++++++++++++++++-- generic/tclInt.decls | 6 +++++- generic/tclIntDecls.h | 8 +++++--- generic/tclStubInit.c | 2 +- generic/tclTest.c | 14 +++++++------- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 26530c3924a3..096ce20e513a 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -23,7 +23,42 @@ #include "tommath.h" #include #include + +/* + * Bug 7371b6270b: to check C call stack depth, prefer an approach which is + * compatible with AddressSanitizer (ASan) use-after-return detection. + */ + +#if defined(HAVE_INTRIN_H) +#include /* for _AddressOfReturnAddress() */ +#endif + +/* + * As suggested by + * https://clang.llvm.org/docs/LanguageExtensions.html#has-builtin + */ +#ifndef __has_builtin +#define __has_builtin(x) 0 /* for non-clang compilers */ +#endif +void * +TclGetCStackPtr(void) +{ +#if defined(HAVE_INTRIN_H) + return _AddressOfReturnAddress(); +#elif __GNUC__ || __has_builtin(__builtin_frame_address) + return __builtin_frame_address(0); +#else + int unused = 0; + /* + * LLVM recommends using volatile: + * https://github.com/llvm/llvm-project/blob/llvmorg-10.0.0-rc1/clang/lib/Basic/Stack.cpp#L31 + */ + int *volatile stackLevel = &unused; + return (void *)stackLevel; +#endif +} + #define INTERP_STACK_INITIAL_SIZE 2000 #define CORO_STACK_INITIAL_SIZE 200 @@ -8824,8 +8859,8 @@ TclNRCoroutineActivateCallback( { CoroutineData *corPtr = (CoroutineData *)data[0]; int type = PTR2INT(data[1]); - int numLevels, unused; - int *stackLevel = &unused; + int numLevels; + void *stackLevel = TclGetCStackPtr(); if (!corPtr->stackLevel) { /* diff --git a/generic/tclInt.decls b/generic/tclInt.decls index ca122f768748..993cc5d613b4 100644 --- a/generic/tclInt.decls +++ b/generic/tclInt.decls @@ -540,6 +540,11 @@ declare 218 { } # for use in tclTest.c + +# Bug 7371b6270b +declare 223 { + void *TclGetCStackPtr(void) +} declare 224 { TclPlatformType *TclGetPlatform(void) } @@ -586,7 +591,6 @@ declare 235 { void TclInitVarHashTable(TclVarHashTable *tablePtr, Namespace *nsPtr) } - # TIP 337 made this one public declare 236 { void TclBackgroundException(Tcl_Interp *interp, int code) diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h index e9587332c856..cb1332753f8d 100644 --- a/generic/tclIntDecls.h +++ b/generic/tclIntDecls.h @@ -532,7 +532,8 @@ EXTERN void TclPopStackFrame(Tcl_Interp *interp); /* Slot 220 is reserved */ /* Slot 221 is reserved */ /* Slot 222 is reserved */ -/* Slot 223 is reserved */ +/* 223 */ +EXTERN void * TclGetCStackPtr(void); /* 224 */ EXTERN TclPlatformType * TclGetPlatform(void); /* 225 */ @@ -875,7 +876,7 @@ typedef struct TclIntStubs { void (*reserved220)(void); void (*reserved221)(void); void (*reserved222)(void); - void (*reserved223)(void); + void * (*tclGetCStackPtr) (void); /* 223 */ TclPlatformType * (*tclGetPlatform) (void); /* 224 */ Tcl_Obj * (*tclTraceDictPath) (Tcl_Interp *interp, Tcl_Obj *rootPtr, int keyc, Tcl_Obj *const keyv[], int flags); /* 225 */ int (*tclObjBeingDeleted) (Tcl_Obj *objPtr); /* 226 */ @@ -1289,7 +1290,8 @@ extern const TclIntStubs *tclIntStubsPtr; /* Slot 220 is reserved */ /* Slot 221 is reserved */ /* Slot 222 is reserved */ -/* Slot 223 is reserved */ +#define TclGetCStackPtr \ + (tclIntStubsPtr->tclGetCStackPtr) /* 223 */ #define TclGetPlatform \ (tclIntStubsPtr->tclGetPlatform) /* 224 */ #define TclTraceDictPath \ diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 9dc3ca0605aa..34bf824ff557 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -720,7 +720,7 @@ static const TclIntStubs tclIntStubs = { 0, /* 220 */ 0, /* 221 */ 0, /* 222 */ - 0, /* 223 */ + TclGetCStackPtr, /* 223 */ TclGetPlatform, /* 224 */ TclTraceDictPath, /* 225 */ TclObjBeingDeleted, /* 226 */ diff --git a/generic/tclTest.c b/generic/tclTest.c index 0061da6b78ee..d05ef7df1afe 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -7390,23 +7390,23 @@ NREUnwind_callback( Tcl_Interp *interp, int result) { - int none; + void *cStackPtr = TclGetCStackPtr(); (void)result; if (data[0] == INT2PTR(-1)) { - Tcl_NRAddCallback(interp, NREUnwind_callback, &none, INT2PTR(-1), + Tcl_NRAddCallback(interp, NREUnwind_callback, cStackPtr, INT2PTR(-1), INT2PTR(-1), NULL); } else if (data[1] == INT2PTR(-1)) { - Tcl_NRAddCallback(interp, NREUnwind_callback, data[0], &none, + Tcl_NRAddCallback(interp, NREUnwind_callback, data[0], cStackPtr, INT2PTR(-1), NULL); } else if (data[2] == INT2PTR(-1)) { Tcl_NRAddCallback(interp, NREUnwind_callback, data[0], data[1], - &none, NULL); + cStackPtr, NULL); } else { Tcl_Obj *idata[3]; idata[0] = Tcl_NewIntObj((int) ((char *) data[1] - (char *) data[0])); idata[1] = Tcl_NewIntObj((int) ((char *) data[2] - (char *) data[0])); - idata[2] = Tcl_NewIntObj((int) ((char *) &none - (char *) data[0])); + idata[2] = Tcl_NewIntObj((int) ((char *) cStackPtr - (char *) data[0])); Tcl_SetObjResult(interp, Tcl_NewListObj(3, idata)); } return TCL_OK; @@ -7452,10 +7452,10 @@ TestNRELevels( (void)objv; if (refDepth == NULL) { - refDepth = &depth; + refDepth = (ptrdiff_t *)TclGetCStackPtr(); } - depth = (refDepth - &depth); + depth = (refDepth - (ptrdiff_t *)TclGetCStackPtr()); levels[0] = Tcl_NewIntObj(depth); levels[1] = Tcl_NewIntObj(iPtr->numLevels);