-1

The man page man 2 time says:

SYNOPSIS
       #include <time.h>
       time_t time(time_t *t);

RETURN VALUE
       On success, the value of time in seconds since  the  Epoch  is  returned.   On  error,
       ((time_t) -1) is returned, and errno is set appropriately.

ERRORS
       EFAULT t points outside your accessible address space.

I've got a simple test set up, trying to coerce an error from that function - should be the most straightforward thing ever:

time_t *ptr = (time_t *) 0xabad1dea;
time_t secs = time(ptr);

if (secs == ((time_t) -1)) /* Not caught */
    exit(EXIT_FAILURE);

printf("Number of seconds since the Epoch:        %lld\n", secs);
printf("It should return ((time_t) -1) on error:  %lld\n", (time_t) -1);
secs = *ptr; /* This *does* segfault */

For some reason, time() is returning -14 instead of -1 on my system. I'm completely stumped. Baffled, even.

I doubt anyone can reproduce this issue, but does anybody out there know why on earth I might be getting that behaviour? If so, I'm terribly curious!

EDIT: I know time isn't dereferencing the bad pointer I pass it, because that crashes the program. And the man page does say it should return -1 and set errno if the address isn't reachable. EFAULT is 14, but isn't returning -EFAULT not what time() is defined as doing here?

jen-rose
  • 133
  • 6
  • you're getting EFAULT, as expected, which on linux is error #14 – Marc B Feb 02 '15 at 20:42
  • @MarcB could be it, but it would go against the manual, which states that *errno* is set to EFAULT and (time_t)-1 is returned. – Alexei Averchenko Feb 02 '15 at 20:50
  • @MarcB: Setting `errno` to `EFAULT` might be expected. *Returning* `-EFAULT` is not. – Keith Thompson Feb 02 '15 at 21:09
  • 1
    `"%lld"` expects an argument of type `long long`. Try casting the argument: `printf("... %lld\n", (long long)secs);`. Hmm. If `time_t` is narrower than `long long` on your system, `printf` might be picking up the `-14` from adjacent memory. – Keith Thompson Feb 02 '15 at 21:36

2 Answers2

3

according to docs time(2):

time_t time(time_t *t);

If t is non-NULL, the return value is also stored in the memory pointed to by t.

In your call, you are passing some random address 0xabad1dea. You're lucky it's only returning -14 and not doing something even nastier. Either pass NULL or the address of a variable.

user590028
  • 11,364
  • 3
  • 40
  • 57
1

There are at least 3 diff places where the behavior of the time() function is documented.

The ISO C standard says:

The time function returns the implementation’s best approximation to the current calendar time. The value (time_t)(-1) is returned if the calendar time is not available. If timer is not a null pointer, the return value is also assigned to the object it points to.

Other wording in the standard makes it clear that passing an invalid argument (which is what you're doing) has undefined behavior. time() is required to handle null pointers, but not invalid ones.

POSIX says:

Upon successful completion, time() shall return the value of time. Otherwise, (time_t)-1 shall be returned.

which doesn't allow for a result other than the current time or (time_t)(-1). On the other hand, POSIX also says:

The functionality described on this reference page is aligned with the ISO C standard. Any conflict between the requirements described here and the ISO C standard is unintentional. This volume of POSIX.1-2008 defers to the ISO C standard.

which could be taken to imply that the behavior of time() with an invalid argument is undefined, as it is in C.

The Linux man page says:

On success, the value of time in seconds since the Epoch is returned. On error, ((time_t) -1) is returned, and errno is set appropriately.

It also explicitly says that errno is set to EFAULT if "t points outside your accessible address space". So it defines the behavior in a case where the behavior is left undefined by the standard, and that's a perfectly valid thing for an implementation to do.

The weird thing is that it returns -14 (-EFAULT) rather than setting errno to EFAULT. Returning a negated errno value is a common convention for functions inside the Linux kernel. The wrapper in the C runtime normally negates that return value and uses it to set errno, setting the return value to -1 (or whatever the defined error value is for the particular function). That's what I'd expect to happen here.

Another weird thing: On my system (Linux Mint 17, x86_64), calling time() with an invalid pointer as you did causes my program to die with a segmentation fault.

I suspect a bug on your system (with a particularly easy workaround: Don't Do That).


UPDATE : Another possibility occurs to me. Your printf calls are incorrect, or at least non-portable. You're printing time_t values with a "%lld" format, which requires an argument of type long long. You should cast the argument to long long:

printf("Number of seconds since the Epoch:        %lld\n",
       (long long)secs);
printf("It should return ((time_t) -1) on error:  %lld\n",
       (long long)((time_t) -1));

If time_t and long long are the same widths on your system, it will probably work without the casts (but you should add them anyway). But if time_t is narrower than long long, printf could be picking up the stray -14 value from adjacent memory.

What are the sizes of time_t and long long on your system? Does casting the arguments change the behavior?

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • I think you're right, this only really makes sense as a bug in the time function. – jen-rose Feb 02 '15 at 21:18
  • @kebertx: Maybe -- but see the last paragraph of my updated answer. You might also update your question with more information about your system. – Keith Thompson Feb 02 '15 at 21:42
  • 1
    @KeithThompson: Looking at [glibc.git:sysdeps/unix/sysv/linux/time.c](https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/time.c;hb=HEAD), this is an obvious glibc bug. The Linux kernel returns error values as negative codes, and instead of checking, the glibc `time()` implementation assumes it cannot fail. – Nominal Animal Feb 03 '15 at 03:06
  • @NominalAnimal: It just occurred to me that `-14` is a valid result -- or it was once. – Keith Thompson Feb 03 '15 at 03:45
  • :) *Wed Dec 31 23:59:46 UTC 1969*. Assuming current epoch. Still, kernel's EFAULT return value is -14, so it's definitely glibc failing to convert it to (time_t)-1 with errno set to 14 (EFAULT). – Nominal Animal Feb 03 '15 at 04:13
  • @Keith - and might be again in 2038! – Robert R Evans Mar 30 '16 at 16:51
  • @RobertREvans: Amusing, but not actually correct. Assuming a 32-bit `time_t` with 2's-complement wraparound semantics, the value won't reach `-14` until Sun 2106-02-07 06:28:02 UTC. But any systems using 32-bit `time_t` will be *very* old by then, and will have been misbehaving for the previous 68 years. – Keith Thompson Mar 30 '16 at 19:15