uClibc thread-safety analysis By Steve Thayer with updates by Erik Andersen Introduction: The purpose of this document is to identify the things that need to be done to the uClibc C library in order to make it thread-safe. The goal is to be able to use a pthreads thread implementation under uClinux, using the uClibc C library. To help identify the things that require changing, I used David R. Butenhof's book Programming With POSIX Threads, the source code for the glibc 2.1.3 C library, and the source code for the C library included in the Proventhreads distribution. References: Butenhof, David R., Programming With POSIX Threads, Addison Wesley Longman, Inc., Reading, MA, ISBN 0-201-63392-2, 1997. The GNU C library is available from the Free Software Foundation http://www.gnu.org/software/libc/libc.html Proventhreads is part of the Inferno Operating system. http://www.vitanuova.com/inferno/index.html 1. Stdio: 1.1 Buffer access mutexes The following functions are required in order to protect shared I/O buffers from being accessed by more than one thread at a time. None of these functions are currently implemented in the uClibc library, so they must be added. flockfile <--- ftrylockfile <--- funlockfile <--- 1.2 Functions that must use buffer access mutexes, according to Butenhof The following functions are identified by Butenhof as needing to use buffer access mutexes. This does not represent all functions that need to use the mutexes. getc <--- getchar <--- putc <--- putchar <--- 1.3 Functions from glibc (libio) that use buffer access mutexes The following functions are functions found in glibc that currently use the buffer access mutexes. Comments in brackets represent status of uClibc with regard to these functions. Most of these functions aren't even supported under uClibc, so no work is required. The rest may require the access mutex. (These must be analyzed on a case-by-case basis.) clearerr <--- feof <--- ferror <--- fputc <--- fputwc freopen <--- freopen64 fseek <--- fseeko fseeko64 ftello ftello64 fwide getc getchar getwc getwchar iofclose iofflush iofgetpos iofgetpos64 iofgets iofgetws iofputs iofputws iofread iofsetpos iofsetpos64 ioftell iofwrite iogetdelim iogets ioputs ioseekoff ioseekpos iosetbuffer iosetvbuf ioungetc ioungetwc oldiofclose oldiofgetpos oldiofgetpos64 oldiofsetpos oldiofsetpos64 peekc putc putchar putwc putwchar rewind <--- 1.4 Functions from Proventhreads that use buffer access mutexes See description above. This applies only to the C library included in the proventhreads distribution. clearerr <--- fclose <--- fflush <--- fgetc <--- __getc fgetline fgetpos <--- fgets <--- fpurge fputc <--- __putc fputs <--- fread <--- freopen <--- fscanf <--- fseek <--- ftell <--- fwalk fwrite <--- getc getchar <--- putc putchar <--- puts <--- putw refill rewind <--- scanf <--- setvbuf <--- ungetc <--- vfprintf <--- vscanf <--- 1.5 Unlocked buffer access These functions get used in situations where speed is important, and locking isn't necessary, or for use in a section of code that is already locked. For example, a for-loop that makes multiple calls to getc could make an exlicit call to flockfile outside the loop, and then use getc_unlocked within the loop for speed. That way, the lock only needs to be grabbed and released once, rather than for each call. getc_unlocked <--- getchar_unlocked <--- putc_unlocked <--- putchar_unlocked <--- 1.6 Additional unlocked calls made in glibc These are additional functions (not mentioned by Butenhof) that the glibc library uses for unlocked buffer access. Though not strictly necessary, these may be nice to have in uClibc. fileno_unlocked <--- clearerr_unlocked <--- feof_unlocked <--- ferror_unlocked <--- fputc_unlocked <--- fgetc_unlocked <--- fflush_unlocked <--- fread_unlocked <--- fwrite_unlocked <--- fgets_unlocked <--- fputs_unlocked <--- 1.7 Additional unlocked calls made in Proventhreads Proventhreads only provides the four unlocked function calls above. 2. Thread-safe functions: There are some functions in the C library standard that are simply not thread-safe and cannot be thread-safe using their current interface. For example, any function that returns a pointer to static data, or requires static context between calls. To resolve this problem, new functions were added to the standard that perform the same basic task, but use a new interface that does not require static data. These functions share their name with the old unsafe functions, but have an "_r" appended to the name. By definition, these functions are reentrant and thread-safe. It is important to note that these functions to do not depend on and can be used even if threading is not supported. 2.1 User and terminal identification: getlogin_r ctermid (1) ttyname_r <--- 1. ctermid is a special case. The signature has not changed, but a requirement has been added that its parameter point to a structure of exactly L_ctermid bytes. 2.2 Directory searching readdir_r <--- 2.3 String token strtok_r 2.4 Time representation asctime_r ctime_r gmtime_r localtime_r 2.5 Random number generation rand_r <--- 2.6 Group and user database getgrgid_r <--- getgrnam_r <--- getpwuid_r getpwnam_r 2.7 Additional thread-safe functions implemented by glibc The following is a list of additional functions implemented by glibc that also provide thread-safe functionality. Most of these functions are not supported by uClibc, so there is no need to implement the thread-safe version. Those that do apply, but have not been implemented yet are highlighted. __fgetpwent_r fgetpwent_r __ttyname_r getttyname_r __getmntent_r getmntent_r <--- ecvt_r fcvt_r qecvt_r qfcvt_r hcreate_r hdestroy_r hsearch_r __getspent_r getspent_r __getspnam_r getspnam_r __sgetspent_r sgetspent_r __fgetspent_r fgetspent_r __gethostbyaddr_r gethostbyaddr_r <--- __gethostbyname2_r gethostbyname2_r __gethostbyname_r gethostbyname_r <--- __gethostent_r __getnetbyaddr_r getnetbyaddr_r <--- __getnetent_r getnetent_r <--- __getnetbyname_r getnetbyname_r <--- __getprotobynumber_r getprotobynumber_r <--- __getprotoent_r getprotoent_r <--- __getprotobyname_r getprotobyname_r <--- __getservbyname_r getservbyname_r <--- __getservbyport_r getservbyport_r <--- __getservent_r getservent_r <--- __getrpcent_r getrpcent_r <--- __getrpcbyname_r getrpcbyname_r <--- __getrpcbynumber_r getrpcbynumber_r <--- ether_aton_r ether_ntoa_r __getnetgrent_r __internal_getnetgrent_r getnetgrent_r __getaliasent_r getaliasent_r __getaliasbyname_r getaliasbyname_r __nscd_getpwnam_r __nscd_getpwuid_r nscd_getpw_r __nscd_getgrgid_r __nscd_getgrnam_r nscd_getgr_r __nscd_gethostbyaddr_r __nscd_gethostbyname2_r __nscd_gethostbyname_r nscd_gethst_r __getutent_r <--- getutent_r <--- getutent_r_unknown getutid_r_unknown getutline_r_unknown __getutid_r getutid_r <--- __getutline_r getutline_r <--- getutent_r_file getutid_r_file getutline_r_file internal_getut_r getutent_r_daemon getutid_r_daemon getutline_r_daemon __ptsname_r ptsname_r 2.8 Additional thread-safe functions implemented by Proventhreads See description above. This applies only to the C library included in the proventhreads distribution. inet_ntoa_r <--- gethostbyaddr_r <--- gethostbyname_r <--- gethostent_r getnetbyaddr_r <--- getnetbyname_r <--- getnetent_r <--- getprotobynumber_r <--- getprotoent_r <--- getprotobyname_r <--- getservbyname_r <--- getservbyport_r <--- getservent_r <--- 3. List of functions in uClibc that use static data structures The following is a list of static data structures found in uClibc, and the functions that use them. In most cases, static data is not thread-safe, since multiple threads can access the same data at the same time. This is an attempt to identify all of the static data that is not considered thread-safe. Some of these problems will get resolved by the changes outlines above. crypt/crypt.c: static struct crypt_data __crypt_data; crypt setkey encrypt -------------------------------------------------------------------- crypt/md5.c: static unsigned char PADDING[64] <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- inet/addr.c: static char buf[16]; inet_ntoa <--- -------------------------------------------------------------------- inet/getnetent.c: static FILE *netf = NULL; static char line[BUFSIZ+1]; static struct netent net; static char *net_aliases[MAXALIASES]; setnetent <--- endnetent <--- getnetent <--- NOTE: setnetent and endnetent are not implemented in glibc. Proventhreads uses pthread mutexes to protect this static data. -------------------------------------------------------------------- inet/getproto.c: static FILE *protof = NULL; static char line[BUFSIZ+1]; static struct protoent proto; static char *proto_aliases[MAXALIASES]; static int proto_stayopen; setprotoent <--- endprotoent <--- getprotoent <--- getprotobyname <--- getprotobynumber <--- NOTE: setprotoent and endprotoent are not implemented in glibc. Proventhreads uses pthread mutexes to protect this static data. -------------------------------------------------------------------- inet/getservice.c: static FILE *servf = NULL; static char line[BUFSIZ+1]; static struct servent serv; static char *serv_aliases[MAXALIASES]; static int serv_stayopen; setservent <--- endservent <--- getservent <--- getservbyname <--- getservbyport <--- NOTE: setservent and endservent are not implemented in glibc. Proventhreads uses pthread mutexes to protect this static data. -------------------------------------------------------------------- net/resolv.c: static int id = 1; static int ns = 0; dns_lookup <--- NOTE: dns_lookup is not implemented by glibc or Proventhreads. static struct hostent h; static char namebuf[256]; static struct in_addr in; static struct in_addr *addr_list[2]; gethostbyname <--- static struct hostent h; static char namebuf[256]; static struct in_addr in; static struct in_addr *addr_list[2]; gethostbyaddr <--- static struct hostent h; static struct in_addr in; static struct in_addr *addr_list[2]; static char line[80]; read_etc_hosts <--- NOTE: dns_lookup is not implemented by glibc or Proventhreads. -------------------------------------------------------------------- inet/rpc/auth_none.c: static struct auth_ops ops static struct authnone_private authnone_create <--- authnone_marshal <--- NOTE: This file makes a lot of use of this static variable and also a global allocated authentication structure. Care should be taken in fixing this to make it thread-safe. -------------------------------------------------------------------- inet/rpc/auth_unix.c: static struct auth_ops auth_unix_ops authunix_create <--- marshal_new_auth <--- NOTE: This file makes a lot of use of this static variable and also a global allocated authentication structure. Care should be taken in fixing this to make it thread-safe. -------------------------------------------------------------------- inet/rpc/bindresvport.c: static short port; bindresvport <--- -------------------------------------------------------------------- inet/rpc/clnt_perror.c: static char *buf; static struct rpc_errtab rpc_errlist[] static struct auth_errtab auth_errlist[] NOTE: These static structures all have #if 0 around them, so they do not get compiled in. Hopefully, we don't have to worry about them right now, but prehaps a comment should be added to the code indicating that it is not thread-safe. -------------------------------------------------------------------- inet/rpc/clnt_raw.c: static struct clntraw_private static struct clnt_ops client_ops clntraw_create <--- clntraw_call <--- clntraw_freeres <--- NOTE: This file makes a lot of use of these static variables and also a global allocated client structure. Care should be taken in fixing this to make it thread-safe. -------------------------------------------------------------------- inet/rpc/clnt_simple.c: static struct callrpc_private callrpc <--- -------------------------------------------------------------------- inet/rpc/clnt_tcp.c: static struct clnt_ops tcp_ops clnttcp_create NOTE: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. -------------------------------------------------------------------- inet/rpc/clnt_udp.c: static struct clnt_ops udp_ops clntudp_bufcreate NOTE: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. -------------------------------------------------------------------- inet/rpc/getrpcent.c: static char RPCDB[] <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- inet/rpc/pmap_clnt.c: static struct timeval timeout <--- static struct timeval tottimeout <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- inet/rpc/pmap_getport.c: static struct timeval timeout <--- static struct timeval tottimeout <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- inet/rpc/pmap_rmt.c: static struct timeval timeout <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- inet/rpc/rpc_dtablesize.c: static int size; _rpc_dtablesize <--- -------------------------------------------------------------------- inet/rpc/rpc_prot.c: static struct xdr_discrim reply_dscrm[3] <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- inet/rpc/svc.c: static SVCXPRT **xports; static SVCXPRT *xports[NOFILE]; static struct svc_callout xprt_register <--- xprt_unregister <--- svc_getreqset <--- svc_register <--- svc_unregister <--- svc_callout <--- NOTE: This is intricate code, and care should be taken when making this thread-safe. -------------------------------------------------------------------- inet/rpc/svc_auth.c: static struct svcauthsw <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- net/rpc/svc_raw.c: static struct svcraw_private static struct xp_ops server_ops svcraw_create <--- svcraw_recv <--- svcraw_reply <--- svcraw_getargs <--- svcraw_freeargs <--- NOTE: This is intricate code, and care should be taken when making this thread-safe. -------------------------------------------------------------------- inet/rpc/svc_simple.c: static struct proglst static SVCXPRT *transp; registerrpc <--- universal <--- NOTE: This is intricate code, and care should be taken when making this thread-safe. -------------------------------------------------------------------- inet/rpc/svc_tcp.c: static struct xp_ops svctcp_op static struct xp_ops svctcp_rendezvous_op svctcp_create <--- makefd_xprt <--- NOTE: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. static struct timeval wait_per_try readtcp <--- NOTE: This looks like a bug. This static timeout value is passed by reference to a select() call. According to the linux man page for select: "On Linux, timeout is modified to reflect the amount of time not slept; most other implementations do not do this. This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple selects in a loop without reinitializing it. Consider timeout to be undefined after select returns." Unless the implementation of select is different than that of Linux, this needs to be fixed! -------------------------------------------------------------------- inet/rpc/svc_udp.c: static struct xp_ops svcudp_op (1) svcudp_bufcreate 1: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. -------------------------------------------------------------------- inet/rpc/xdr.c: static char xdr_zero[BYTES_PER_XDR_UNIT] <--- NOTE: This is okay, but should use the const keyword. static u_long crud[BYTES_PER_XDR_UNIT] NOTE: This looks like it doesn't matter what's in this array. -------------------------------------------------------------------- inet/rpc/xdr_float.c: static struct sgl_limits <--- static struct dbl_limits <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- inet/rpc/xdr_mem.c: static struct xdr_ops xdrmem_ops (1) xdrmem_create 1: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. -------------------------------------------------------------------- inet/rpc/xdr_rec.c: static struct xdr_ops xdrrec_ops (1) xdrrec_create 1: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. -------------------------------------------------------------------- inet/rpc/xdr_stdio.c: static struct xdr_ops xdrstdio_ops (1) xdrstdio_create 1: This static structure is just a group of function pointers. It could probably be made const, but this might affect the function signature. This should be investigated further. -------------------------------------------------------------------- ld.so-1/d-link/boot1.c: static char *_dl_malloc_addr static char *_dl_mmap_zero static char *_dl_not_lazy static char *_dl_warn static char *_dl_trace_loaded_objects _dl_boot <--- _dl_malloc <--- _dl_fixup <--- These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/d-link/readelflib1.c: static caddr_t _dl_cache_addr static size_t _dl_cache_size _dl_map_cache <--- _dl_unmap_cache <--- _dl_load_shared_library <--- These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/d-link/string.h: static char local[22] _dl_simple_ltoa <--- _dl_simple_ltoahex <--- These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/d-link/arm/elfinterp.c: static char *_dl_reltypes[] <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- ld.so-1/d-link/i386/elfinterp.c: static char *_dl_reltypes[] <--- NOTE: This is okay, but should use the const keyword. These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/d-link/m68k/elfinterp.c: static char *_dl_reltypes[] <--- NOTE: This is okay, but should use the const keyword. These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/d-link/sparc/elfinterp.c: static char *_dl_reltypes[] <--- NOTE: This is okay, but should use the const keyword. These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/libdl/dlib.c: static int dl_init _dlopen <--- static int __attribute__ ((unused)) foobar1 (1) NOTE: The comment for this says it all: "This is a real hack." ;-) static char *type[] <--- NOTE: This is okay, but should use the const keyword. These are all part of the shared library loader, and are not used by applications directly. Therefore, fixing these is not a high priority item. -------------------------------------------------------------------- ld.so-1/util/ldconfig.c: static header_t magic <--- NOTE: This is okay, but should use the const keyword. static liblist_t *lib_head cache_dolib <--- cache_write <--- This is not actually part of the C library, and is not built by default, so fixing these is not a high priority item. -------------------------------------------------------------------- misc/internals/tempname.c: static uint64_t value; __gen_tempname <--- -------------------------------------------------------------------- misc/locale/locale.c: static char C_LOCALE_NAME[]="C"; <--- NOTE: This is okay, but should use the const keyword. static struct SAV_LOADED_LOCALE sav_loaded_locale [1] static struct SAV_LOADED_LOCALE * old_locale setlocale <--- NOTE: Can different threads use different locales? I don't see why not. -------------------------------------------------------------------- misc/locale/localeconv.c: static struct lconv result; localeconv <--- NOTE: This function returns a pointer to a static data structure. static char *blank = ""; <--- static char *decimal = "."; <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- misc/mntent/mntent.c: static char buff[MNTMAXSTR]; static struct mntent mnt; getmntent <--- -------------------------------------------------------------------- misc/regex/regex.c: static char re_syntax_table[CHAR_SET_SIZE]; static int done = 0; init_syntax_table <--- static int debug; <--- NOTE: This is used to turn on debugging, and is used in several functions. It will need to be fixed if you want differing debug levels per thread. static char reg_unset_dummy; <--- static fail_stack_type fail_stack; regex_compile <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- static int regs_allocated_size; regs_grow_registers <--- static register_info_type *reg_info; static register_info_type *reg_info_dummy; static unsigned failure_id; static struct re_pattern_buffer re_comp_buf; <--- NOTE: This is just a NASTY file for static variables. A lot of work needs to be done here to clean this up. But I'm not even sure if it matters. This code is taken directly from glibc. This code is also very large (adds over 30k to the C library all by itself). This file needs a complete rewrite. -------------------------------------------------------------------- misc/syslog/syslog.c: static pthread_once__t _once_block = pthread_once_init; static pthread_mutex_t _syslog_mutex; NOTE: I think these are okay. ;-) static int LogFile = -1; static int connected; static int LogStat = 0; static int LogFacility = LOG_USER; static int LogMask = 0xff; static char truncate_msg[12] static struct sockaddr SyslogAddr; NOTE: These are already protected. -------------------------------------------------------------------- misc/time/asctime.c: static char timebuf[26]; asctime -------------------------------------------------------------------- misc/time/ctime.c: static char cbuf[26]; ctime -------------------------------------------------------------------- misc/time/gmtime.c: static struct tm tmb; gmtime -------------------------------------------------------------------- misc/time/localtime.c: static struct tm tmb; localtime -------------------------------------------------------------------- misc/time/mktime.c: static tz_rule tz_rules[2]; tzset <--- static time_t localtime_offset; mktime <--- -------------------------------------------------------------------- misc/time/tm_conv.c: static int moffset[] <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- misc/utmp/utent.c: static int ut_fd = -1; setutent <--- endutent <--- getutent <--- getutid <--- getutline <--- pututline <--- utmpname <--- static struct utmp utmp; __getutent <--- -------------------------------------------------------------------- pwd_grp/__getgrent.c: static char line_buff[GR_MAX_LINE_LEN]; static char *members[GR_MAX_MEMBERS]; static char *line_buff = NULL; static char **members = NULL; static struct group group; __getgrent <--- -------------------------------------------------------------------- pwd_grp/fgetpwent.c: static char line_buff[PWD_BUFFER_SIZE]; static struct passwd pwd; fgetpwent -------------------------------------------------------------------- pwd_grp/getpwnam.c: static char line_buff[PWD_BUFFER_SIZE]; static struct passwd pwd; getpwnam -------------------------------------------------------------------- pwd_grp/getpwuid.c: static char line_buff[PWD_BUFFER_SIZE]; static struct passwd pwd; getpwuid -------------------------------------------------------------------- pwd_grp/grent.c: static int grp_fd = -1; setgrent <--- endgrent <--- getgrent <--- -------------------------------------------------------------------- pwd_grp/pwent.c: static int pw_fd = -1; setpwent <--- endpwent <--- getpwent_r <--- NOTE: Yeah, this looks weird, but getpwent_r isn't completely thread-safe. static char line_buff[PWD_BUFFER_SIZE]; static struct passwd pwd; getpwent <--- -------------------------------------------------------------------- stdio/tmpnam.c: static char tmpnam_buffer[L_tmpnam]; tmpnam -------------------------------------------------------------------- stdlib/atexit.c: static vfuncp __atexit_table[__UCLIBC_MAX_ATEXIT]; static int __atexit_count = 0; atexit_handler <--- atexit <--- -------------------------------------------------------------------- stdlib/bsearch.c: static int _bsearch; bsearch <--- -------------------------------------------------------------------- stdlib/putenv.c: static char **mall_env = 0; static int extras = 0; putenv <--- -------------------------------------------------------------------- stdlib/random.c: static long int seed1 = 1; static long int seed2 = 1; static long int seed3 = 1; random (1) srandom (1) 1: I'm not sure if it matters if these are static, since they are random number seeds. Who cares if more than one thread changes their value? -------------------------------------------------------------------- stdlib/setenv.c: static pthread_once__t _once_block = pthread_once_init; (1) static pthread_mutex_t _setenv_mutex; (1) static char **last_environ = NULL; (1) 1: Obviously, nothing to do here. (Unless I change the way we deal with threads). -------------------------------------------------------------------- stdlib/malloc/avlmacro.h static objname *__Avl_##objname##pr##_new_node; Avl_Tree_no_replace <--- NOTE: This will take a bit of study to figure out if it needs fixing. -------------------------------------------------------------------- stdlib/malloc/malloc.c: //static mutex_t malloc_lock = MUTEX_INITIALIZER; <--- NOTE: Basically, thread support in malloc is broken and must be fixed. It looks like the infrastructure is there, but more investigation is required. -------------------------------------------------------------------- stdlib/malloc-930716/malloc.c: static int heapsize; static int initialized; static size_t pagesize; inititalize <--- morecore <--- malloc <--- -------------------------------------------------------------------- stdlib/malloc-930716/valloc.c: static size_t pagesize; valloc <--- -------------------------------------------------------------------- string/config.c: static char *args[16]; static char cfgbuf[128]; cfgread <--- -------------------------------------------------------------------- string/strerror.c: static char retbuf[48]; static char retbuf[33]; strerror <--- main <--- -------------------------------------------------------------------- string/strsignal.c: static char retbuf[28]; strsignal <--- main <--- -------------------------------------------------------------------- string/strtok.c: static char *save = 0; strtok <--- -------------------------------------------------------------------- sysdeps/linux/common/kernel_version.c: static int __linux_kernel_version = -1; __get_linux_kernel_version (1) 1: This static value never actually gets updated! This a bug. -------------------------------------------------------------------- sysdeps/linux/i386/bits/huge_val.h: static __huge_val_t __huge_val <--- static __huge_valf_t __huge_valf <--- static __huge_vall_t __huge_vall <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/i386/bits/nan.h: static union { ... } __nan_union <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/m68k/bits/huge_val.h: static union { ... } __huge_val <--- static union { ... } __huge_valf <--- static union { ... } __huge_vall <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/m68k/bits/nan.h: static union { ... } __nan_union <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/sh/bits/huge_val.h: static __huge_val_t __huge_val <--- static __huge_valf_t __huge_valf <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/sh/bits/nan.h: static union { ... } __nan_union <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/sparc/bits/huge_val.h: static __huge_val_t __huge_val <--- static __huge_valf_t __huge_valf <--- NOTE: These are okay, but should use the const keyword. -------------------------------------------------------------------- sysdeps/linux/sparc/bits/nan.h: static union { ... } __nan_union <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- termios/tcgetsid.c: static int tiocgsid_does_not_work; tcgestsid <--- -------------------------------------------------------------------- termios/ttyname.c: static char dev[] = "/dev"; ttyname <--- NOTE: This is okay, but should use the const keyword. static char name[NAME_MAX]; ttyname <--- -------------------------------------------------------------------- test/testsuite.h: static int failures error_msg <--- done_testing <--- init_testsuite <--- -------------------------------------------------------------------- unistd/getcwd.c: static char *path_buf; static int path_size; static dev_t root_dev; static ino_t root_ino; static struct stat st; getswd <--- recurser <--- search_dir <--- -------------------------------------------------------------------- unistd/getopt.c: static int sp = 1; getopt <--- -------------------------------------------------------------------- unistd/getpass.c: static char buf[PWD_BUFFER_SIZE]; getpass <--- NOTE: This function returns a pointer to a static data structure. This seems like it requires an _r version of this function. Glibc does the same thing. Oops! So much for thread-safe glibc! -------------------------------------------------------------------- unistd/gnu_getopt.c: static char *nextchar; static enum ordering; static int first_nonopt; static int last_nonopt; _getopt_initialize <--- _getopt_internal <--- exchange <--- static struct option long_options[] <--- NOTE: This is okay, but should use the const keyword. -------------------------------------------------------------------- unistd/sysconf.c: static long int ret_vals[_UCLIBC_SYSCONF_NUM_VALID_ARGS]; find_or_add_in_table <--- main <--- NOTE: I'm not sure if this needs to be multi-threaded or not. -------------------------------------------------------------------- unistd/sysconf_src.c: static long int ret_vals[_UCLIBC_SYSCONF_NUM_VALID_ARGS]; find_or_add_in_table <--- main <--- NOTE: I'm not sure if this needs to be multi-threaded or not. -------------------------------------------------------------------- unistd/sysconf_i386.c: static long int ret_vals[_UCLIBC_SYSCONF_NUM_VALID_ARGS]; find_or_add_in_table <--- main <--- NOTE: I'm not sure if this needs to be multi-threaded or not. -------------------------------------------------------------------- 4. List of functions that use global variables The following is a list of functions that make use of global variables. Since multiple threads can access the same global variable at the same time, access should be considered unsafe. This is an attempt to identify all the areas where global variables are used. This does not necessarily mean that each of these is unsafe. It just means that there is a potential for them to be unsafe. If this code never runs in more than one thread, then there's no problem. More ivestigation will be required to determine if changes are really required. Global variable: __environ (misc/internals/__uClibc_main.c) __uClibc_main.c: __uClibc_main (1) 1: This should only get executed once, so it is probably fine. stdlib/getenv.c: getenv <--- stdlib/putenv.c: putenv <--- stdlib/setenv.c: setenv <--- unsetenv <--- test/args/arg_test.c: main <--- unistd/execl.c: execl <--- unistd/execlp.c: execlp <--- unistd/execv.c: execv <--- unistd/execvp.c: execvep <--- -------------------------------------------------------------------- Global variable: __uClibc_cleanup (misc/internals/__uClibc_main.c) stdlib/abort.c: abort <--- stdlib/atexit.c: atexit_handler <--- atexit <--- exit <--- NOTE: Not sure if multiple threads can be in this code or not. -------------------------------------------------------------------- Global variable: environ (misc/internals/__uClibc_main.c) NOTE: This is a weak alias for __environ, but it doesn't ever get used in the uClibc library. -------------------------------------------------------------------- Global variable: timezone (misc/time/tm_conv.c) misc/time/tm_conv.c: __tm_conv <--- -------------------------------------------------------------------- Global variable: re_max_failures (misc/regex/regex.c) misc/regex/regex.c: DOUBLE_FAIL_STACK <--- regex_compile <--- -------------------------------------------------------------------- Global variable: re_syntax_options (misc/regex/regex.c) misc/regex/regex.c: re_set_syntax <--- re_compile_pattern <--- re_comp <--- -------------------------------------------------------------------- Global variable: __IO_list (stdio/stdio.c) stdio/stdio.c: fflush <--- __fopen <--- fclose <--- -------------------------------------------------------------------- Global variable: _fixed_buffers (stdio/stdio.c) stdio/stdio.c: _alloc_stdio_buffer <--- _free_stdio_buffer_of_file <--- __init_stdio <--- -------------------------------------------------------------------- Global variable: _free_buffer_index (stdio/stdio.c) stdio/stdio.c: _alloc_stdio_buffer <--- _free_stdio_buffer_of_file <--- __init_stdio <--- -------------------------------------------------------------------- Global variable: _free_file_list (stdio/stdio.c) stdio/stdio.c: __init_stdio <--- _alloc_stdio_stream <--- _free_stdio_stream <--- -------------------------------------------------------------------- Global variable: _stderr (stdio/stdio.c) ld.so-1/util/ldconfig.c: warn <--- error <--- usage <--- ld.so-1/util/ldd.c: warn <--- error <--- is_bin <--- main <--- misc/locale/locale.c: setlocale <--- misc/regex/regex.c: printchar <--- stdio/perror.c: perror <--- stdlib/malloc/alloc.c: calloc_dbg <--- malloc_dbg <--- free_dbg <--- stdlib/malloc/malloc.c: __hunk_alloc (1) __malloc_init (1) malloc (1) 1: These are commented out with C++ style comments. stdlib/malloc-simple/alloc.c: calloc_dbg <--- malloc_dbg <--- free_dbg <--- string/strsignal.c: psignal <--- test/args/arg_test.c: main <--- test/assert/assert.c: main <--- unistd/getopt.c: Err <--- unistd/getpass.c: getpass <--- unistd/gnu_getopt.c: _getopt_internal <--- unistd/sysconf.c: main <--- unistd/sysconf_src.c: main <--- unistd/sysconf_i386.c: main <--- -------------------------------------------------------------------- Global variable: _stdin (stdio/stdio.c) include/stdio.h: getchar <--- include/bits/stdio.h: getchar <--- getchar_unlocked <--- stdio/scanf.c: scanf <--- vscanf <--- stdio/stdio.c gets <--- getchar <--- sysdeps/linux/i386/bits/stdio.h: getchar <--- getchar_unlocked <--- sysdeps/linux/m68k/bits/stdio.h: getchar <--- getchar_unlocked <--- sysdeps/linux/sh/bits/stdio.h: getchar <--- getchar_unlocked <--- sysdeps/linux/sparc/bits/stdio.h: getchar <--- getchar_unlocked <--- unistd/getpass.c: getpass <--- -------------------------------------------------------------------- Global variable: _stdio_streams (stdio/stdio.c) stdio/stdio.c: __init_stdio <--- _free_stdio_stream <--- -------------------------------------------------------------------- Global variable: _stdout (stdio/stdio.c) include/stdio.h: putchar <--- include/bits/stdio.h: vprintf <--- putchar <--- putchar_unlocked <--- ld.so-1/util/ldconfig.c: warn <--- error <--- ld.so-1/util/ldd.c: warn <--- error <--- main <--- stdio/printf.c: printf <--- vprintf <--- stdio/stdio.c: puts <--- _uClibc_fread <--- putchar <--- sysdeps/linux/i386/bits/stdio.h: vprintf <--- putchar <--- putchar_unlocked <--- sysdeps/linux/m68k/bits/stdio.h: vprintf <--- putchar <--- putchar_unlocked <--- sysdeps/linux/sh/bits/stdio.h: vprintf <--- putchar <--- putchar_unlocked <--- sysdeps/linux/sparc/bits/stdio.h: vprintf <--- putchar <--- putchar_unlocked <--- test/pwd_grp/test_grp.c: main <--- test/pwd_grp/test_pwd.c: main <--- -------------------------------------------------------------------- Global variable: dns_caught_signal (inet/resolv.c) inet/resolv.c: dns_catch_signal <--- dns_lookup <--- -------------------------------------------------------------------- Global variable: nameserver (inet/resolv.c) nameservers (inet/resolv.c) inet/resolv.c: open_nameservers <--- close_nameservers <--- resolve_name <--- gethostbyname <--- res_query <--- gethostbyaddr <--- -------------------------------------------------------------------- Global variable: searchdomain (inet/resolv.c) searchdomains (inet/resolv.c) inet/resolv.c: dns_lookup <--- -------------------------------------------------------------------- Global variable: _net_stayopen (inet/getnetent.c) inet/getnetbyad.c: getnetbyaddr <--- inet/getnetbynm.c: getnetbyname <--- inet/getnetent.c: setnetent <--- endnetent <--- -------------------------------------------------------------------- Global variable: rpcdata (inet/rpc/getrpcent.c) inet/rpc/getrpcent.c: _rpcdata <--- -------------------------------------------------------------------- Global variable: _null_auth (inet/rpc/rpc_commondata.c) <--- NOTE: _null_auth is never actually initialized. It never gets written, only read. So it should be thread safe. But it should be declared as a const if that is the case. It should also be initialized. inet/rpc/auth_none.c: authnone_create inet/rpc/auth_unix.c: authunix_create inet/rpc/clnt_raw.c: clntraw_call inet/rpc/clnt_tcp.c: clnttcp_call inet/rpc/clnt_udp.c: clntudp_call inet/rpc/pmap_rmt.c: clnt_broadcast inet/rpc/svc_auth.c: _authenticate inet/rpc/svc_tcp.c: svctcp_create -------------------------------------------------------------------- Global variable: rpc_createerr (inet/rpc/rpc_commondata.c) inet/rpc/clnt_generic.c: clnt_create <--- inet/rpc/clnt_perror.c: clnt_spcreateerror <--- NOTE: This piece of code has an "#if 0" around it. inet/rpc/clnt_simple.c: callrpc <--- inet/rpc/clnt_tcp.c: clnttcp_create <--- inet/rpc/clnt_udp.c: clntudp_bufcreate <--- inet/rpc/pmap_getport.c: pmap_getport <--- -------------------------------------------------------------------- Global variable: svc_fdset (inet/rpc/rpc_commondata.c) inet/rpc/svc.c: xprt_register <--- xprt_unregister <--- svc_getreq <--- inet/rpc/svc_run.c: svc_run <--- NOTE: Be careful to also fix the uses of the "svc_fds" #define. -------------------------------------------------------------------- Global variable: pl (inet/rpc/svc_simple.c) registerrpc <--- NOTE: proglst is set up to point at pl, so it needs fixing as well. (See proglst earlier in this document.) -------------------------------------------------------------------- Global variable: _sigintr (signal/signal.c) signal/bsd_sig.c: __bsd_signal <--- signal/sigintr.c: siginterrupt <--- -------------------------------------------------------------------- Global variable: __Avl_Block_tfree_mem_tree (stdlib/malloc/malloc.c) stdlib/malloc/malloc.c: __free_mem_del_block <--- bl_alloc <--- __malloc_init <--- NOTE: This code is very tricky stuff. -------------------------------------------------------------------- Global variable: __Avl_Block_tptrs_tree (stdlib/malloc/malloc.c) stdlib/malloc/malloc.c: __bl_free <--- __malloc_init <--- free <--- _realloc_no_move <--- realloc <--- -------------------------------------------------------------------- Global variable: __bl_last (stdlib/malloc/malloc.c) stdlib/malloc/malloc.c: COMBINE_BLOCKS <--- SPLIT_BLOCK <--- bl_mapnew <--- bl_alloc <--- __malloc_init <--- -------------------------------------------------------------------- Global variable: __free_h (stdlib/malloc/malloc.c) stdlib/malloc/malloc.c: __hunk_alloc <--- __hunk_free <--- __malloc_init <--- -------------------------------------------------------------------- Global variable: __malloc_initialized (stdlib/malloc/malloc.c) stdlib/malloc/malloc.c: __malloc_init <--- malloc <--- free <--- _realloc_no_move <--- -------------------------------------------------------------------- Global variable: __total_h (stdlib/malloc/malloc.c) stdlib/malloc/malloc.c: __hunk_alloc <--- __malloc_init <--- -------------------------------------------------------------------- Global variable: errno (sysdeps/linux/common/errno.c) sysdeps/linux/common/errno.c: __errno_location <--- NOTE: Obviously, errno gets used all over the place. I won't list them all here. -------------------------------------------------------------------- Global variable: ___brk_addr (sysdeps/linux/i386/__init_brk.c) sysdeps/linux/i386/__init_brk.c: __init_brk <--- sysdeps/linux/i386/brk.c: brk <--- sysdeps/linux/i386/sbrk.c: sbrk <--- -------------------------------------------------------------------- Global variable: optarg (unistd/getopt_vars.c) extra/locale/gen_ctype_from_glibc.c: main (1) ld.so-1/util/ldconfig.c: main (1) unistd/getopt.c: getopt <--- unistd/gnu_getopt.c: _getopt_internal <--- main (1) 1: Probably not required unless this program is run on multiple threads. -------------------------------------------------------------------- Global variable: opterr (unistd/getopt_vars.c) ld.so-1/util/ldconfig.c: main (1) unistd/getopt.c: Err <--- getopt <--- unistd/gnu_getopt.c: _getopt_internal <--- 1: Probably not required unless this program is run on multiple threads. -------------------------------------------------------------------- Global variable: optind (unistd/getopt_vars.c) extra/locale/gen_ctype_from_glibc.c: main (1) ld.so-1/util/ldconfig.c: main (1) ld.so-1/util/ldd.c: main (1) unistd/getopt.c: Err <--- getopt <--- unistd/gnu_getopt.c: exchange <--- _getopt_initialize <--- _getopt_internal <--- main (1) main (2nd one) (1) 1: Probably not required unless this program is run on multiple threads. -------------------------------------------------------------------- Global variable: optopt (unistd/getopt_vars.c) unistd/getopt.c: Err <--- getopt <--- unistd/gnu_getopt.c: _getopt_internal <---