[PATCH] linuxthreads.old: fix nommu initial thread stack detection

Steven J. Magnani steve at digidescorp.com
Tue Feb 22 16:35:29 UTC 2011


Mike -

Nice catch. Some commenting suggestions below.

Reviewed-by: Steven J. Magnani <steve at digidescorp.com>


On Tue, 2011-02-22 at 00:31 +0000, uclibc-request at uclibc.org wrote:
> Message: 2
> Date: Mon, 21 Feb 2011 17:51:04 -0500
> From: Mike Frysinger <vapier at gentoo.org>
> To: uclibc at uclibc.org
> Cc: toolchain-devel at blackfin.uclinux.org
> Subject: [PATCH] linuxthreads.old: fix nommu initial thread stack
> 	detection
> Message-ID: <1298328664-24563-1-git-send-email-vapier at gentoo.org>
> 
> Because the nommu address space is flat, and the application stack can
> literally be located anywhere, we cannot rely on the assumptions that the
> mmu port gets away with.  Namely, that the first thread's stack lives at
> the top of memory and nothing will be created above it.
> 
> Currently, the code rounds the current stack up a page and sets that as
> the "top" of the stack, and then marks the "bottom" of the stack as "1".
> Then as new threads are created, this assumption is further refined by
> slowly backing off the "bottom" when a new stack is created within the
> range of the initial stack.
> 
> Simple ascii example (tid0 is the initial thread):
> 
> 1 thread:
>  [bos          tid0 stack           tos]
> 
> 2 threads:
>                  [     tid0 stack      ]
>       [tid1 stack]
> 
> 3 threads:
>                  [     tid0 stack      ]
>       [tid1 stack]
>                                             [tid2 stack]
> 
> As you can kind of see, this algorithm operates on one basic assumption:
> the initial top of stack calculation is the absolute top of the stack.
> While this assumption was fairly safe in the original nommu days of yore
> where the only file format was FLAT (which defaults to a 4KiB stack --
> exactly 1 page), and memory was fairly tight, we can see that this falls
> apart pretty quickly as soon as the initial stack is larger than a page.
> 
> The issue that crops up now is simple to hit: start an application with
> an 8KiB stack, execute some functions that put pressure on the stack so
> that it exceeds 4KiB, then start up some threads.  The initial tos will
> be rounded up by a page, but this is actually the middle of the stack.
> Now when the initial thread returns from its functions (thus unwinding
> the stack) and tries to call something which calls back into libpthread,
> the thread_self() func fails to detect itself as the initial thread as
> the current stack is now above the tos.  The __pthread_find_self() func
> kicks in, walks all the thread arrays, fails to find a hit, and then
> walks into uninitialized memory for the thread descriptor.  Use of this
> garbage memory has obvious results -- things fall down & go boom.
> 
> To address this, I extend the current algorithm to automatically scale
> back both the bottom and the top stack limits of the initial thread.
> We use the current stack pointer at "thread boot time" only as a single
> known point.  The initial thread stack bottom is set to the bottom of
> memory and the initial thread stack top is set to the top of memory.
> Then as we create new stack threads, we figure out whether the new stack
> is above or below the single known good address, and then scale back
> either the tos or the bos accordingly.
> 
> Signed-off-by: Mike Frysinger <vapier at gentoo.org>
> ---
>  libpthread/linuxthreads.old/internals.h |   16 ++++++++++++----
>  libpthread/linuxthreads.old/pthread.c   |   13 ++++++++-----
>  2 files changed, 20 insertions(+), 9 deletions(-)
> 
> diff --git a/libpthread/linuxthreads.old/internals.h b/libpthread/linuxthreads.old/internals.h
> index 637fcea..2999baf 100644
> --- a/libpthread/linuxthreads.old/internals.h
> +++ b/libpthread/linuxthreads.old/internals.h
> @@ -258,11 +258,19 @@ extern pthread_descr __pthread_main_thread;
>  
>  extern char *__pthread_initial_thread_bos;
>  #ifndef __ARCH_USE_MMU__
> -extern char *__pthread_initial_thread_tos;
> +extern char *__pthread_initial_thread_tos, *__pthread_initial_thread_mid;

A comment here describing the _mid logic would be helpful down the road
- maybe something that paraphrases the last paragraph of your changelog.

>  #define NOMMU_INITIAL_THREAD_BOUNDS(tos,bos) \
> -	if ((tos)>=__pthread_initial_thread_bos \
> -	    && (bos)<__pthread_initial_thread_tos) \
> -		__pthread_initial_thread_bos = (tos)+1
> +	do { \
> +		char *__tos = (tos); \
> +		char *__bos = (bos); \
> +		if (__tos >= __pthread_initial_thread_bos && \
> +		    __bos < __pthread_initial_thread_tos) { \
> +			if (__bos < __pthread_initial_thread_mid) \
> +				__pthread_initial_thread_bos = __tos; \
> +			else \
> +				__pthread_initial_thread_tos = __bos; \
> +		} \
> +	} while (0)
>  #else
>  #define NOMMU_INITIAL_THREAD_BOUNDS(tos,bos) /* empty */
>  #endif /* __ARCH_USE_MMU__ */
> diff --git a/libpthread/linuxthreads.old/pthread.c b/libpthread/linuxthreads.old/pthread.c
> index ad392e3..3878984 100644
> --- a/libpthread/linuxthreads.old/pthread.c
> +++ b/libpthread/linuxthreads.old/pthread.c
> @@ -174,6 +174,7 @@ char *__pthread_initial_thread_bos = NULL;
>  
>  #ifndef __ARCH_USE_MMU__
>  char *__pthread_initial_thread_tos = NULL;
> +char *__pthread_initial_thread_mid = NULL;
>  #endif /* __ARCH_USE_MMU__ */
>  
>  /* File descriptor for sending requests to the thread manager. */
> @@ -457,12 +458,14 @@ static void pthread_initialize(void)
>      setrlimit(RLIMIT_STACK, &limit);
>    }
>  #else
> -  /* For non-MMU assume __pthread_initial_thread_tos at upper page boundary, and
> -   * __pthread_initial_thread_bos at address 0. These bounds are refined as we
> -   * malloc other stack frames such that they don't overlap. -StS
> +  /* For non-MMU we need to start the initial thread stack for all available
> +   * memory.  These bounds are refined as we malloc other stack frames such
> +   * that they don't overlap.  We cannot pick an arbitrary tos point (such as
> +   * rounding up a page) as it might be slightly crossing page boundaries in
> +   * the middle of this code execution.
>     */

How about:

  /* For non-MMU, the initial thread stack can reside anywhere in memory.
   * All we can determine here is an address that lies within that stack.
   * Save that address as a reference so that as other thread stacks are
   * created, we can adjust the estimated bounds of the initial thread's
   * stack appropriately.
   */

> -  __pthread_initial_thread_tos =
> -    (char *)(((long)CURRENT_STACK_FRAME + getpagesize()) & ~(getpagesize() - 1));
> +  __pthread_initial_thread_mid = (long)CURRENT_STACK_FRAME;
> +  __pthread_initial_thread_tos = (char *) -1;
>    __pthread_initial_thread_bos = (char *) 1; /* set it non-zero so we know we have been here */
>    PDEBUG("initial thread stack bounds: bos=%p, tos=%p\n",
>  	 __pthread_initial_thread_bos, __pthread_initial_thread_tos);
> -- 
> 1.7.4.1

------------------------------------------------------------------------
 Steven J. Magnani               "I claim this network for MARS!
 www.digidescorp.com              Earthling, return my space modulator!"

 #include <standard.disclaimer>




More information about the uClibc mailing list