Do not impose an arbitrary file name length limit on platforms like GNU/Hurd that do not define PATH_MAX. Conversely, when PATH_MAX is defined (e.g., GNU/Linux, the BSDs), speed things up a bit if the TZ string is very long. * NEWS: Mention this. * localtime.c (union local_storage.fullname): Now of size PATH_MAX if PATH_MAX is defined, and removed otherwise. All uses changed. (tzloadbody) [!PATH_MAX]: If the TZ string is too long, allocate a larger buffer instead of failing with ENAMETOOLONG. This happens only with artificial TZ strings like "America/./././Los_Angeles" except with many more "/."s. This supports the GNU/Hurd philosophy of no arbitrary limits. (tzloadbody) [PATH_MAX]: Speed things up a bit. (tzload): Call ‘free’ even if !ALL_STATE, if tzloadbody allocated a larger buffer. With decent optimization this call is optimized away if PATH_MAX && !ALL_STATE (the default case on most platforms). --- NEWS | 4 ++++ localtime.c | 35 ++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 5b00817f..64d9336d 100644 --- a/NEWS +++ b/NEWS @@ -77,6 +77,10 @@ Unreleased, experimental changes tzset etc. now have an experimental OPENAT_TZDIR option; see Makefile and localtime.c for details. + On platforms like GNU/Hurd that do not define PATH_MAX, + exceedingly long TZ strings no longer fail merely because they + exceed an arbitrary file name length limit imposed by tzcode. + Changes to commentary The leapseconds file contains commentary about the IERS and NIST diff --git a/localtime.c b/localtime.c index c7b9f663..7ab8aa41 100644 --- a/localtime.c +++ b/localtime.c @@ -689,13 +689,10 @@ union local_storage { struct state st; } u; - /* The name of the file to be opened. Ideally this would have no - size limits, to support arbitrarily long Zone names. - Limiting Zone names to 1024 bytes should suffice for practical use. - However, there is no need for this to be smaller than struct - file_analysis as that struct is allocated anyway, as the other - union member. */ - char fullname[max(sizeof(struct file_analysis), sizeof TZDIR + 1024)]; +#ifdef PATH_MAX + /* The name of the file to be opened. */ + char fullname[PATH_MAX]; +#endif }; /* These tzload flags can be ORed together, and fit into 'char'. */ @@ -788,25 +785,35 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, if (!OPENAT_TZDIR && !SUPPRESS_TZDIR && name[0] != '/') { char *cp; - size_t namesizemax = sizeof lsp->fullname - tzdirslashlen; + size_t fullnamesize; +#ifdef PATH_MAX + static_assert(tzdirslashlen <= PATH_MAX); + size_t namesizemax = PATH_MAX - tzdirslashlen; size_t namelen = strnlen (name, namesizemax); if (namesizemax <= namelen) return ENAMETOOLONG; +#else + size_t namelen = strlen (name); +#endif + fullnamesize = tzdirslashlen + namelen + 1; /* Create a string "TZDIR/NAME". Using sprintf here would pull in stdio (and would fail if the resulting string length exceeded INT_MAX!). */ - if (ALL_STATE) { - lsp = malloc(sizeof *lsp); + if (ALL_STATE || sizeof *lsp <= fullnamesize) { + lsp = malloc(max(sizeof *lsp, fullnamesize)); if (!lsp) return HAVE_MALLOC_ERRNO ? errno : ENOMEM; *lspp = lsp; } - cp = lsp->fullname; - cp = mempcpy(cp, tzdirslash, tzdirslashlen); + cp = mempcpy(lsp, tzdirslash, tzdirslashlen); cp = mempcpy(cp, name, namelen); *cp = '\0'; +#ifdef PATH_MAX name = lsp->fullname; +#else + name = (char *) lsp; +#endif } fid = OPENAT_TZDIR ? openat(dd, relname, oflags) : open(name, oflags); @@ -1086,6 +1093,7 @@ static int tzload(char const *name, struct state *sp, char tzloadflags) { int r; + union local_storage *lsp0; union local_storage *lsp; #if ALL_STATE lsp = NULL; @@ -1093,8 +1101,9 @@ tzload(char const *name, struct state *sp, char tzloadflags) union local_storage ls; lsp = &ls; #endif + lsp0 = lsp; r = tzloadbody(name, sp, tzloadflags, &lsp); - if (ALL_STATE) + if (lsp != lsp0) free(lsp); return r; } -- 2.51.0