From b30c5c185556bd0c48fbfa3bb7fa6f76158024af Mon Sep 17 00:00:00 2001 From: Landon Curt Noll Date: Mon, 6 Dec 2021 21:16:08 -0800 Subject: [PATCH] Improved seed() generation Drop support for CLOCK_SGI_CYCLE. Improved seed() generation. Improved some comments in seed.c. Added have_environ.c to build have_environ.h in order to determine if: extern char **environ; /* user environment */ is an valid external symbol. --- .gitignore | 1 + CHANGES | 9 +++ Makefile.ship | 67 ++++++++++++++++-- have_gettime.c | 17 ++--- help/seed | 6 +- seed.c | 186 ++++++++++++++++++++++++++++++++++--------------- win32.mkdef | 1 + 7 files changed, 213 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 1c66da5..9e3363b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ endian_calc.h fposval.h have_ban_pragma.h have_const.h +have_environ.h have_fpos.h have_fpos_pos.h have_getpgid.h diff --git a/CHANGES b/CHANGES index fa1a54e..424b8d8 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ The following are the changes from calc version 2.14.0.11 to date: Fixed a number of typos. Drop support for SunOS, IRIX and MINGW32_NT-5.0 targets. + Drop support for CLOCK_SGI_CYCLE. Minor improvement of various help files. Made format of help files more consistent. @@ -10,6 +11,14 @@ The following are the changes from calc version 2.14.0.11 to date: Corrected a few comments in zrandom.c, including a case where the comment referred to 1007 when it should have used 2^32. + Improved seed() generation. Improved some comments in seed.c. + Added have_environ.c to build have_environ.h in order to + determine if: + + extern char **environ; /* user environment */ + + is an valid external symbol. + The following are the changes from calc version 2.14.0.9 to 2.14.0.10: diff --git a/Makefile.ship b/Makefile.ship index 3334de4..53c21fd 100644 --- a/Makefile.ship +++ b/Makefile.ship @@ -379,6 +379,19 @@ HAVE_CONST= HAVE_UID_T= #HAVE_UID_T= -DHAVE_NO_UID_T +# Determine if we have user environment external: +# +# extern char **environ; +# +# If HAVE_ENVIRON is empty, this Makefile will run the have_environ program +# to determine if environ exsuts. If HAVE_ENVIRON is set to -DHAVE_NO_ENVIRON, +# then calc will assume there is no external environ symbol. +# +# If in doubt, leave HAVE_ENVIRON empty and this Makefile will figure it out. +# +HAVE_ENVIRON= +#HAVE_ENVIRON= -DHAVE_NO_ENVIRON + # Determine if we have memcpy(), memset() and strchr() # # If HAVE_NEWSTR is empty, this Makefile will run the have_newstr program @@ -1914,7 +1927,7 @@ BUILD_H_SRC= align32.h args.h calcerr.h conf.h endian_calc.h \ have_posscl.h have_rusage.h have_stdlib.h have_strdup.h \ have_string.h have_strlcat.h have_strlcpy.h have_times.h \ have_uid_t.h have_unistd.h have_unused.h have_urandom.h \ - have_ustat.h longbits.h terminal.h + have_ustat.h longbits.h terminal.h have_environ.h # we build these .c files during the make # @@ -1927,7 +1940,7 @@ BUILD_C_SRC= calcerr.c UTIL_C_SRC= align32.c endian.c longbits.c have_newstr.c have_uid_t.c \ have_const.c have_stdvs.c have_varvs.c fposval.c have_fpos.c \ have_fpos_pos.c have_offscl.c have_posscl.c have_memmv.c \ - have_ustat.c have_getsid.c have_getpgid.c \ + have_ustat.c have_getsid.c have_getpgid.c have_environ.c \ have_gettime.c have_getprid.c have_rusage.c have_strdup.c \ have_unused.c have_ban_pragma.c have_strlcpy.c have_strlcat.c @@ -1944,7 +1957,7 @@ UTIL_MISC_SRC= calcerr_h.sed calcerr_h.awk calcerr_c.sed calcerr_c.awk \ UTIL_OBJS= endian.o longbits.o have_newstr.o have_uid_t.o \ have_const.o fposval.o have_fpos.o have_fpos_pos.o \ try_strarg.o have_stdvs.o have_varvs.o have_posscl.o have_memmv.o \ - have_ustat.o have_getsid.o have_getpgid.o \ + have_ustat.o have_getsid.o have_getpgid.o have_environ.o \ have_gettime.o have_getprid.o ver_calc.o have_rusage.o have_strdup.o \ have_unused.o have_ban_pragma.o have_strlcpy.o have_strlcat.o @@ -1961,7 +1974,7 @@ UTIL_PROGS= align32${EXT} fposval${EXT} have_uid_t${EXT} have_const${EXT} \ endian${EXT} longbits${EXT} have_newstr${EXT} have_stdvs${EXT} \ have_varvs${EXT} have_ustat${EXT} have_getsid${EXT} \ have_getpgid${EXT} have_gettime${EXT} have_getprid${EXT} \ - ver_calc${EXT} have_strdup${EXT} \ + ver_calc${EXT} have_strdup${EXT} have_environ{EXT} \ have_unused${EXT} have_fpos${EXT} have_fpos_pos${EXT} \ have_offscl${EXT} have_rusage${EXT} have_ban_pragma${EXT} \ have_strlcpy${EXT} have_strlcat${EXT} @@ -3248,6 +3261,47 @@ have_uid_t.h: have_uid_t.c have_unistd.h \ ${TRUE}; \ fi +have_environ.h: have_environ.c \ + banned.h have_ban_pragma.h ${MAKE_FILE} ${LOC_MKF} + ${Q} ${RM} -f have_environ environ_tmp $@ + ${H} echo 'forming $@' + ${Q} echo '/*' > $@ + ${Q} echo ' * DO NOT EDIT -- generated by the Makefile' >> $@ + ${Q} echo ' */' >> $@ + ${Q} echo '' >> $@ + ${Q} echo '' >> $@ + ${Q} echo '#if !defined(CALC_HAVE_ENVIRON_H)' >> $@ + ${Q} echo '#define CALC_HAVE_ENVIRON_H' >> $@ + ${Q} echo '' >> $@ + ${Q} echo '' >> $@ + ${Q} echo '/* do we have or want environ? */' >> $@ + ${Q} ${RM} -f have_environ.o have_environ${EXT} + -${Q} ${LCC} ${ICFLAGS} ${HAVE_ENVIRON} have_environ.c -c ${S} \ + || ${TRUE} + -${Q} ${LCC} ${ILDFLAGS} have_environ.o -o have_environ${EXT} ${S} \ + || ${TRUE} + -${Q} ./have_environ${EXT} > environ_tmp ${E} \ + || ${TRUE} + -${Q} if [ -s environ_tmp ]; then \ + ${CAT} environ_tmp >> $@; \ + else \ + echo '#undef HAVE_ENVIRON /* no */' >> $@; \ + fi + ${Q} echo '' >> $@ + ${Q} echo '' >> $@ + ${Q} echo '#endif /* !CALC_HAVE_ENVIRON_H */' >> $@ + ${Q} ${RM} -f have_environ${EXT} have_environ.o environ_tmp + ${H} echo '$@ formed' + -@if [ -z "${Q}" ]; then \ + echo ''; \ + echo '=-=-= start of $@ =-=-='; \ + ${CAT} $@; \ + echo '=-=-= end of $@ =-=-='; \ + echo ''; \ + else \ + ${TRUE}; \ + fi + have_newstr.h: have_newstr.c banned.h have_ban_pragma.h ${MAKE_FILE} ${LOC_MKF} ${Q} ${RM} -f newstr_tmp $@ ${H} echo 'forming $@' @@ -4425,6 +4479,7 @@ env: @echo 'GREP=${GREP}'; echo '' @echo 'H=${H}'; echo '' @echo 'HAVE_CONST=${HAVE_CONST}'; echo '' + @echo 'HAVE_ENVIRON=${HAVE_ENVIRON}'; echo '' @echo 'HAVE_FPOS=${HAVE_FPOS}'; echo '' @echo 'HAVE_FPOS_POS=${HAVE_FPOS_POS}'; echo '' @echo 'HAVE_GETPGID=${HAVE_GETPGID}'; echo '' @@ -5817,6 +5872,9 @@ have_ban_pragma.o: have_ban_pragma.h have_const.o: banned.h have_const.o: have_ban_pragma.h have_const.o: have_const.c +have_environ.o: banned.h +have_environ.o: have_ban_pragma.h +have_environ.o: have_environ.c have_fpos.o: banned.h have_fpos.o: have_ban_pragma.h have_fpos.o: have_fpos.c @@ -6456,6 +6514,7 @@ seed.o: decl.h seed.o: endian_calc.h seed.o: have_ban_pragma.h seed.o: have_const.h +seed.o: have_environ.h seed.o: have_getpgid.h seed.o: have_getprid.h seed.o: have_getsid.h diff --git a/have_gettime.c b/have_gettime.c index 39a462f..afc1277 100644 --- a/have_gettime.c +++ b/have_gettime.c @@ -34,10 +34,9 @@ * This prog outputs several defines: * * HAVE_GETTIME - * defined ==> use clock_gettime() for either CLOCK_SGI_CYCLE - * and/or CLOCK_REALTIME - * undefined ==> clock_gettime() is not available for both - * CLOCK_SGI_CYCLE and CLOCK_REALTIME + * defined ==> use clock_gettime() from CLOCK_REALTIME + * undefined ==> clock_gettime() is not available from + * CLOCK_REALTIME */ #include @@ -56,13 +55,7 @@ main(void) #else /* HAVE_NO_GETTIME */ -# if defined(CLOCK_SGI_CYCLE) - - struct timespec sgi_cycle; /* SGI hardware clock */ - (void) clock_gettime(CLOCK_SGI_CYCLE, &sgi_cycle); - printf("#define HAVE_GETTIME /* yes - w/CLOCK_SGI_CYCLE */\n"); - -# elif defined(CLOCK_REALTIME) +# if defined(CLOCK_REALTIME) struct timespec realtime; /* POSIX realtime clock */ (void) clock_gettime(CLOCK_REALTIME, &realtime); @@ -70,7 +63,7 @@ main(void) # else - printf("#undef HAVE_GETTIME /* no - no SGI_CYCLE and no REALTIME */\n"); + printf("#undef HAVE_GETTIME /* no - no CLOCK_REALTIME */\n"); # endif /* CLOCK_REALTIME */ diff --git a/help/seed b/help/seed index 83a5289..1bac5b4 100644 --- a/help/seed +++ b/help/seed @@ -25,8 +25,8 @@ DESCRIPTION sequence each time they are run. The return value of this builtin function should NOT be considered - a random or pseudo-random value. The return value should be used - as an argument to a seed function such as srand() or srandom(). + a random or pseudo-random value. The return value could be used + as an argument to seed functions such as srand() or srandom(). EXAMPLE ; print srand(seed()) @@ -44,7 +44,7 @@ LINK LIBRARY SEE ALSO seed, srand, randbit, isrand, rand, random, srandom, israndom -## Copyright (C) 1999,2018 Landon Curt Noll +## Copyright (C) 1999,2018,2021 Landon Curt Noll ## ## Calc is open software; you can redistribute it and/or modify it under ## the terms of the version 2.1 of the GNU Lesser General Public License diff --git a/seed.c b/seed.c index f1e6216..2a6677a 100644 --- a/seed.c +++ b/seed.c @@ -1,5 +1,5 @@ /* - * seed - produce a pseudo-random seeds + * seed - produce pseudo-random seeds * * Copyright (C) 1999-2007,2021 Landon Curt Noll * @@ -90,6 +90,7 @@ #include "have_urandom.h" #include "have_rusage.h" #include "have_uid_t.h" +#include "have_environ.h" #if defined(HAVE_USTAT) # include #endif @@ -108,13 +109,25 @@ */ #if defined(HAVE_B64) typedef USB64 hash64; +static hash64 prev_hash64 = 0; /* previous pseudo_seed() return or 0 */ #else struct s_hash64 { USB32 w32[2]; }; typedef struct s_hash64 hash64; +static hash64 prev_hash64 = { 0, 0 }; /* previous pseudo_seed() return or 0 */ #endif +#if defined(HAVE_ENVIRON) +extern char **environ; /* user environment */ +#endif /* HAVE_ENVIRON */ + + +/* + * call counter - number of times pseudo_seed() as been called + */ +static FULL call_count = 0; + /* * 64-bit initial basis - Based on the 64-bit FNV basis value @@ -134,16 +147,16 @@ typedef struct s_hash64 hash64; * hashed. * * Yes, even with this non-zero virgin value there is a set of data - * that will result in a zero hash value. Worse, appending any - * about of zero bytes will continue to produce a zero hash value. - * But that would happen with any initial value so long as the - * hash of the initial was the `inverse' of the virgin prefix string. + * that will result in a zero hash value. Worse, appending some + * mount of zero value bytes will continue to produce a zero hash value. + * But that would happen with any initial value so long as the hash + * was the hash `inverse' of the virgin prefix string. * * But then again for any hash function, there exists sets of data * which that the hash of every member is the same value. That is - * life with many to few mapping functions. All we do here is to - * prevent sets whose members consist of 0 or more bytes of 0's from - * being such an awkward set. + * life with mapping functions. All we do here is to prevent sets + * whose members consist of 0 or more bytes of 0's from being such + * an awkward set. * * And yes, someone can figure out what the magic 'inverse' of the * 32 ASCII character are ... but this hash function is NOT intended @@ -160,9 +173,67 @@ typedef struct s_hash64 hash64; #endif +/* + * initial_private_hash64 - initial basis of Fowler/Noll/Vo-1 64 bit hash + */ +S_FUNC hash64 +initial_private_hash64(void) +{ + hash64 hval; /* current hash value */ +#if defined(HAVE_B64) + hval = PRIVATE_64_BASIS; +#else /* HAVE_B64 */ + USB32 val[4]; /* hash value in base 2^16 */ + + /* hash each octet of the buffer */ + val[0] = PRIVATE_64_BASIS_0; + val[1] = PRIVATE_64_BASIS_1; + val[2] = PRIVATE_64_BASIS_2; + val[3] = PRIVATE_64_BASIS_3; + + /* convert to hash64 */ + /* hval.w32[1] = 0xffff&(val[3]<<16)+val[2]; */ + hval.w32[1] = (val[3]<<16) + val[2]; + hval.w32[0] = (val[1]<<16) + val[0]; +#endif /* HAVE_B64 */ + + /* return our initial hash value */ + return hval; +} + + /* * private_hash64_buf - perform a Fowler/Noll/Vo-1 64 bit hash * + * FNV-1 - Fowler/Noll/Vo-1 64 bit hash + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an Email message + * to Landon, they named it ``Fowler/Noll/Vo'' or the FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/ + * + * for more details as well as other forms of the FNV hash. + * + * NOTE: For general hash functions, we recommend using the + * FNV-1a hash function. The use of FNV-1 is kept + * for backwards compatibility purposes and because + * the use of FNV-1 in this special purpose, suffices. + * * input: * buf - start of buffer to hash * len - length of buffer in octets @@ -172,48 +243,18 @@ typedef struct s_hash64 hash64; * 64 bit hash as a static hash64 structure */ S_FUNC hash64 -private_hash64_buf(char *buf, unsigned len) +private_hash64_buf(hash64 hval, char *buf, unsigned len) { - hash64 hval; /* current hash value */ #if !defined(HAVE_B64) USB32 val[4]; /* hash value in base 2^16 */ USB32 tmp[4]; /* tmp 64 bit value */ #endif /* HAVE_B64 */ char *buf_end = buf+len; /* beyond end of hash area */ - /* - * FNV-1 - Fowler/Noll/Vo-1 64 bit hash - * - * The basis of this hash algorithm was taken from an idea sent - * as reviewer comments to the IEEE POSIX P1003.2 committee by: - * - * Phong Vo (http://www.research.att.com/info/kpv/) - * Glenn Fowler (http://www.research.att.com/~gsf/) - * - * In a subsequent ballot round: - * - * Landon Curt Noll (http://www.isthe.com/chongo/) - * - * improved on their algorithm. Some people tried this hash - * and found that it worked rather well. In an Email message - * to Landon, they named it ``Fowler/Noll/Vo'' or the FNV hash. - * - * FNV hashes are designed to be fast while maintaining a low - * collision rate. The FNV speed allows one to quickly hash lots - * of data while maintaining a reasonable collision rate. See: - * - * http://www.isthe.com/chongo/tech/comp/fnv/ - * - * for more details as well as other forms of the FNV hash. - * - * NOTE: For general hash functions, we recommend using the - * FNV-1a hash function. The use of FNV-1 is kept - * for backwards compatibility purposes and because - * the use of FNV-1 in this special purpose, suffices. - */ #if defined(HAVE_B64) + /* hash each octet of the buffer */ - for (hval = PRIVATE_64_BASIS; buf < buf_end; ++buf) { + for (; buf < buf_end; ++buf) { /* multiply by 1099511628211ULL mod 2^64 using 64 bit longs */ hval *= (hash64)1099511628211ULL; @@ -224,11 +265,12 @@ private_hash64_buf(char *buf, unsigned len) #else /* HAVE_B64 */ - /* hash each octet of the buffer */ - val[0] = PRIVATE_64_BASIS_0; - val[1] = PRIVATE_64_BASIS_1; - val[2] = PRIVATE_64_BASIS_2; - val[3] = PRIVATE_64_BASIS_3; + /* load val array from hval argument */ + val[0] = hval.w32[0] & 0xffff; + val[1] = (hval.w32[0]>>16) & 0xffff; + val[2] = hval.w32[1] & 0xffff; + val[3] = (hval.w32[1]>>16) & 0xffff; + for (; buf < buf_end; ++buf) { /* @@ -241,7 +283,7 @@ private_hash64_buf(char *buf, unsigned len) /* multiply by the lowest order digit base 2^16 */ tmp[0] = val[0] * 0x1b3; tmp[1] = val[1] * 0x1b3; - tmp[2] = val[2] * 0x1b3; + tmp[1] = val[2] * 0x1b3; tmp[3] = val[3] * 0x1b3; /* multiply by the other non-zero digit */ tmp[2] += val[0] << 8; /* tmp[2] += val[0] * 0x100 */ @@ -299,9 +341,6 @@ pseudo_seed(void) { struct { /* data used for quasi-random seed */ #if defined(HAVE_GETTIME) -# if defined(CLOCK_SGI_CYCLE) - struct timespec sgi_cycle; /* SGI hardware clock */ -# endif # if defined(CLOCK_REALTIME) struct timespec realtime; /* POSIX realtime clock */ # endif @@ -359,25 +398,36 @@ pseudo_seed(void) #endif time_t time; /* local time */ size_t size; /* size of this data structure */ + hash64 prev_hash64_copy; /* copy if the previous hash value */ + FULL call_count_copy; /* count pf this funcation was called */ jmp_buf env; /* setjmp() context */ +#if defined(HAVE_ENVIRON) + char **environ_copy; /* copy of extern char **environ */ +#endif /* HAVE_ENVIRON */ char *sdata_p; /* address of this structure */ } sdata; hash64 hash_val; /* fnv64 hash of sdata */ ZVALUE hash; /* hash_val as a ZVALUE */ NUMBER *ret; /* return seed as a NUMBER */ +#if defined(HAVE_ENVIRON) + int i; + size_t envlen; /* length of an environment variable */ +#endif + + /* + * initialize the Fowler/Noll/Vo-1 64 bit hash + */ + hash_val = initial_private_hash64(); /* * pick up process/system information * * NOTE: - * We do care (that much) if these calls fail. We do not + * We do NOT care (that much) if these calls fail. We do not * need to process any data in the 'sdata' structure. */ memset(&sdata, 0, sizeof(sdata)); #if defined(HAVE_GETTIME) -# if defined(CLOCK_SGI_CYCLE) - (void) clock_gettime(CLOCK_SGI_CYCLE, &sdata.sgi_cycle); -# endif # if defined(CLOCK_REALTIME) (void) clock_gettime(CLOCK_REALTIME, &sdata.realtime); # endif @@ -441,13 +491,34 @@ pseudo_seed(void) #endif sdata.time = time(NULL); sdata.size = sizeof(sdata); + sdata.prev_hash64_copy = prev_hash64; + sdata.call_count_copy = ++call_count; (void) setjmp(sdata.env); +#if defined(HAVE_ENVIRON) + sdata.environ_copy = environ; +#endif /* HAVE_ENVIRON */ sdata.sdata_p = (char *)&sdata; /* * seed the generator with the above data */ - hash_val = private_hash64_buf((char *)&sdata, sizeof(sdata)); + hash_val = private_hash64_buf(hash_val, (char *)&sdata, sizeof(sdata)); + +#if defined(HAVE_ENVIRON) + /* + * seed each envinment variable + */ + for (i=0; environ[i] != NULL; ++i) { + + /* obtain length of this next environment variable string */ + envlen = strlen(environ[i]); + + /* hash any non-zero length environment variable string */ + if (envlen > 0) { + hash_val = private_hash64_buf(hash_val, environ[i], envlen); + } + } +#endif /* HAVE_ENVIRON */ /* * load the hash data into the ZVALUE @@ -461,6 +532,11 @@ pseudo_seed(void) memcpy((void *)hash.v, (void *)&hash_val, hash.len*sizeof(HALF)); ztrim(&hash); + /* + * save hash value for next call + */ + prev_hash64 = hash_val; + /* * return a number */ diff --git a/win32.mkdef b/win32.mkdef index 2c50dee..1bcb634 100644 --- a/win32.mkdef +++ b/win32.mkdef @@ -32,6 +32,7 @@ HAVE_SYS_TIMES_H=NO HAVE_TIME_H=YES HAVE_SYS_TIME_H=NO HAVE_UNISTD_H=NO +HAVE_ENVIRON=-DHAVE_NO_ENVIRON BINDIR=/usr/bin/calc LIBDIR=/lib/calc