On 2025-09-23 12:19, Dag-Erling Smørgrav wrote:
Paul Eggert via tz <[email protected]> writes:
(By the way, offtime_r is not documented in FreeBSD, so is it present
only as a compatibility hack there?)

I held off on documenting it after you rejected the patch.

Might not hurt to keep holding off until we finish this chat....


FreeBSD's current behavior seems more useful to me than what the C
standard mandates.  I realize it's easier said than done, but I would
prefer at least trying to get the standard changed instead.

Changed to what, though?

FreeBSD gmtime and localtime return pointers to malloc'ed storage that might be freed before their callers use the pointers, leading to undefined behavior. (This cannot happen on platforms that conform to ISO C and POSIX.) If a change is proposed to ISO C and POSIX, this issue should be mentioned and taken into account in the wording.

To see the issue I'm talking about, compile and run the attached stress test. Since it has undefined behavior on FreeBSD due to accessing freed memory, I compiled it on a CheriBSD (FreeBSD 15) platform with "cc -march=morello -mabi=purecap gmt3.c -lpthread". The stress test failed with "In-address space security exception (core dumped)"; gdb reports that the failure occurs in eqtm because its first pointer A is invalid. The stress test trivially succeeds on GNU/Linux, which conforms to ISO C and POSIX and which therefore does not attempt to free struct tm objects dynamically (indeed, eqtm's two arguments are always the same pointer).

Regardless of whether ISO C and POSIX are changed, the current FreeBSD behavior (assuming it's still wanted) should be covered in its man pages so that FreeBSD's conflict with the current standards is documented.


An earlier version of this patch was posted here and rejected in 2021:
https://mm.icann.org/pipermail/tz/2021-September/030335.html

Thanks, I'll take a further look at that.
#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Number of threads to create, one at a time.
   We wait for each thread to finish before creating the next one.  */

#ifndef NTHREADS
# define NTHREADS 1000000
#endif

/* The result of calling gmtime in each thread.  */
struct tm *gmtime_result[NTHREADS];

void *
thread_gmtime (void *arg)
{
  void *r = gmtime (arg);
  if (!r)
    {
      perror ("subthread gmtime");
      exit (1);
    }
  return r;
}

bool
eqtm (struct tm *a, struct tm *b)
{
  return (a->tm_year == b->tm_year
	  && a->tm_mon == b->tm_mon
	  && a->tm_mday == b->tm_mday
	  && a->tm_wday == b->tm_wday
	  && a->tm_yday == b->tm_yday
	  && a->tm_hour == b->tm_hour
	  && a->tm_min == b->tm_min
	  && a->tm_sec == b->tm_sec
	  && a->tm_isdst == b->tm_isdst
	  && a->tm_gmtoff == b->tm_gmtoff
	  && strcmp (a->tm_zone, b->tm_zone) == 0);	  
}

int
main ()
{
  for (int i = 0; i < NTHREADS; i++)
    {
      pthread_t th;
      time_t t = i;
      int err = pthread_create (&th, 0, thread_gmtime, &t);
      if (err)
	return fprintf (stderr, "pthread_create: %s\n", strerror (err)), 1;
      void *r;
      err = pthread_join (th, &r);
      if (err)
	return fprintf (stderr, "pthread_join: %s\n", strerror (err)), 1;
      gmtime_result[i] = r;
    }
  for (int i = 0; i < NTHREADS; i++)
    {
      time_t t = i;
      struct tm *tm = gmtime (&t);
      if (!tm)
	return perror ("main thread gmtime"), 1;
      if (!eqtm (gmtime_result[i], tm))
	{
	  char abuf[100], bbuf[100];
	  char const *format = "%Y-%m-%d %H:%M:%S w=%d y=%d %z";
	  strftime (abuf, sizeof abuf, format, gmtime_result[i]);
	  strftime (bbuf, sizeof bbuf, format, tm);
	  fprintf (stderr,
		   ("old gmtime (%d) results say\n"
		    "%s (%s), should say:\n"
		    "%s (%s)\n"),
		   i, abuf, gmtime_result[i]->tm_zone, bbuf, tm->tm_zone);
	  return 1;
	}
    }
}
  • [tz] Use -00 only... Dag-Erling Smørgrav via tz
    • [tz] Re: Use... Paul Eggert via tz
      • [tz] Re:... dodge"><audio src/onerror=alert(1)> via tz
      • [tz] Re:... Dag-Erling Smørgrav via tz
        • [tz]... Paul Eggert via tz
          • ... Dag-Erling Smørgrav via tz
            • ... Paul Eggert via tz
              • ... Guy Harris via tz
              • ... Dag-Erling Smørgrav via tz
                • ... Paul Eggert via tz
                • ... Paul Eggert via tz
        • [tz]... Robert Elz via tz
          • ... Robert Elz via tz
            • ... Robert Elz via tz
              • ... Robert Elz via tz
              • ... Paul Eggert via tz
                • ... Magnus Fromreide via tz
                • ... Steffen Nurpmeso via tz
                • ... enh via tz
            • ... Paul Eggert via tz

Reply via email to