In ArgusSource.c around line 2235, inside the ArgusSourceProcess function, we see how it calculates the timeout:
gettimeofday (tvp, 0L);
tts->tv_sec = tvp->tv_sec + 0;
tts->tv_nsec = (tvp->tv_usec * 1000) + 250000000;
if (tts->tv_nsec > 1000000000) {
tts->tv_sec++;
tts->tv_nsec -= 1000000000;
}
gettimeofday returns the current time in seconds and microseconds (tv_usec).
The code converts microseconds to nanoseconds (by multiplying by 1,000) and adds 250,000,000 nanoseconds (0.25 seconds) to set a future timeout.
The bug: If gettimeofday happens to fetch a time where the microsecond value is exactly 750000, the math evaluates to: (750000 * 1000) + 250000000 = 1,000,000,000.
The if statement checks if the result is strictly greater than > 1000000000. Since 1,000,000,000 is not greater than itself, it skips the rollover logic.
It then passes exactly 1,000,000,000 to pthread_cond_timedwait(), which violates the POSIX rule requiring nanoseconds to be strictly less than 1,000,000,000. The function immediately rejects it and returns EINVAL, crashing the thread.
The comparison should be
if (tts->tv_nsec >= 1000000000) {
a dtrace probe seems to confirm
#!/usr/sbin/dtrace -s
/* Watch for entry to capture the timespec pointer (arg2) */
pid$target::pthread_cond_timedwait:entry
{
self->ts_ptr = arg2;
}
/* Watch for the return. If it returns 22 (EINVAL), trigger the output */
pid$target::pthread_cond_timedwait:return
/arg1 == 22/
{
printf("\npthread_cond_timedwait returned EINVAL!\n");
/* Attempt to read the timespec struct (tv_sec and tv_nsec) */
this->ts = (struct timespec *)copyin(self->ts_ptr, sizeof(struct timespec));
printf("tv_sec: %Y\n", this->ts->tv_sec);
printf("tv_nsec: %d\n", this->ts->tv_nsec);
}
# dtrace -s awatch.d -p 9585
dtrace: script 'awatch.d' matched 3 probes
dtrace: pid 9585 exited with status 1
CPU ID FUNCTION:NAME
21 87869 pthread_cond_timedwait:return
pthread_cond_timedwait returned EINVAL!
tv_sec: 1969 Dec 31 19:00:01
tv_nsec: 1000000000
libthr.so.3`pthread_cond_timedwait+0x20
argus`0x2230cf
libc.so.7`__libc_start1+0x150
argus`0x221864
`0x1b5d97a03008
In ArgusSource.c around line 2235, inside the ArgusSourceProcess function, we see how it calculates the timeout:
gettimeofday (tvp, 0L); tts->tv_sec = tvp->tv_sec + 0; tts->tv_nsec = (tvp->tv_usec * 1000) + 250000000; if (tts->tv_nsec > 1000000000) { tts->tv_sec++; tts->tv_nsec -= 1000000000; }gettimeofday returns the current time in seconds and microseconds (tv_usec).
The code converts microseconds to nanoseconds (by multiplying by 1,000) and adds 250,000,000 nanoseconds (0.25 seconds) to set a future timeout.
The bug: If gettimeofday happens to fetch a time where the microsecond value is exactly 750000, the math evaluates to: (750000 * 1000) + 250000000 = 1,000,000,000.
The if statement checks if the result is strictly greater than > 1000000000. Since 1,000,000,000 is not greater than itself, it skips the rollover logic.
It then passes exactly 1,000,000,000 to pthread_cond_timedwait(), which violates the POSIX rule requiring nanoseconds to be strictly less than 1,000,000,000. The function immediately rejects it and returns EINVAL, crashing the thread.
The comparison should be
if (tts->tv_nsec >= 1000000000) {
a dtrace probe seems to confirm
#!/usr/sbin/dtrace -s /* Watch for entry to capture the timespec pointer (arg2) */ pid$target::pthread_cond_timedwait:entry { self->ts_ptr = arg2; } /* Watch for the return. If it returns 22 (EINVAL), trigger the output */ pid$target::pthread_cond_timedwait:return /arg1 == 22/ { printf("\npthread_cond_timedwait returned EINVAL!\n"); /* Attempt to read the timespec struct (tv_sec and tv_nsec) */ this->ts = (struct timespec *)copyin(self->ts_ptr, sizeof(struct timespec)); printf("tv_sec: %Y\n", this->ts->tv_sec); printf("tv_nsec: %d\n", this->ts->tv_nsec); } # dtrace -s awatch.d -p 9585 dtrace: script 'awatch.d' matched 3 probes dtrace: pid 9585 exited with status 1 CPU ID FUNCTION:NAME 21 87869 pthread_cond_timedwait:return pthread_cond_timedwait returned EINVAL! tv_sec: 1969 Dec 31 19:00:01 tv_nsec: 1000000000 libthr.so.3`pthread_cond_timedwait+0x20 argus`0x2230cf libc.so.7`__libc_start1+0x150 argus`0x221864 `0x1b5d97a03008