Skip to content

Commit d40ffac

Browse files
committed
Add clock_gettime model
Add a library model for `clock_gettime`: - Model follows POSIX specification with proper error handling - Non-deterministic behavior covers both success and failure paths - Validates timespec structure with appropriate constraints - Sets errno correctly (EFAULT for null pointer, EINVAL on failure) Fixes: #7639
1 parent 8640fc9 commit d40ffac

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include <assert.h>
2+
#include <errno.h>
3+
#include <stdint.h>
4+
#include <time.h>
5+
6+
// MSVC doesn't have CLOCK_MONOTONIC
7+
#ifndef CLOCK_MONOTONIC
8+
# define CLOCK_MONOTONIC 1
9+
#endif
10+
11+
// Test function from the issue description
12+
uint32_t my_time(void)
13+
{
14+
struct timespec t;
15+
int ret = clock_gettime(CLOCK_MONOTONIC, &t);
16+
17+
// If clock_gettime fails, return a safe value
18+
if(ret != 0)
19+
{
20+
return 0;
21+
}
22+
23+
return (t.tv_nsec / 1000000) + ((t.tv_sec % 86400) * 1000);
24+
}
25+
26+
int main()
27+
{
28+
// Test null pointer behavior
29+
int result_null = clock_gettime(CLOCK_REALTIME, 0);
30+
if(result_null == -1)
31+
{
32+
// errno should be set to EFAULT for null pointer
33+
assert(errno == EFAULT);
34+
}
35+
36+
struct timespec ts;
37+
38+
// Test basic functionality with different clock types
39+
int result1 = clock_gettime(CLOCK_REALTIME, &ts);
40+
assert(result1 == 0 || result1 == -1);
41+
42+
if(result1 == 0)
43+
{
44+
// If successful, tv_nsec should be in valid range
45+
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999L);
46+
}
47+
48+
// Test with CLOCK_MONOTONIC
49+
int result2 = clock_gettime(CLOCK_MONOTONIC, &ts);
50+
assert(result2 == 0 || result2 == -1);
51+
52+
if(result2 == 0)
53+
{
54+
// If successful, tv_nsec should be in valid range
55+
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999L);
56+
}
57+
58+
// Test the my_time function from the issue
59+
// Note: my_time() handles the failure case internally
60+
uint32_t time_result = my_time();
61+
// Should return either 0 (on failure) or valid millisecond value within a day
62+
assert(time_result < 86400000 || time_result == 0);
63+
64+
return 0;
65+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--pointer-check --bounds-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/library/time.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,68 @@ __CPROVER_size_t _strftime(
280280
s[length] = '\0';
281281
return length;
282282
}
283+
284+
/* FUNCTION: clock_gettime */
285+
286+
#ifndef __CPROVER_TIME_H_INCLUDED
287+
# include <time.h>
288+
# define __CPROVER_TIME_H_INCLUDED
289+
#endif
290+
291+
#ifndef __CPROVER_ERRNO_H_INCLUDED
292+
# include <errno.h>
293+
# define __CPROVER_ERRNO_H_INCLUDED
294+
#endif
295+
296+
int __VERIFIER_nondet_int(void);
297+
time_t __VERIFIER_nondet_time_t(void);
298+
long __VERIFIER_nondet_long(void);
299+
300+
int clock_gettime(clockid_t clockid, struct timespec *tp)
301+
{
302+
__CPROVER_HIDE:;
303+
304+
// Use the clockid parameter (all clock types are modeled the same way)
305+
(void)clockid;
306+
307+
// Check for null pointer - should set errno to EFAULT
308+
// Some systems have C headers where `tp` is annotated to be nonnull
309+
#pragma GCC diagnostic push
310+
#pragma GCC diagnostic ignored "-Wnonnull-compare"
311+
if(!tp)
312+
{
313+
errno = EFAULT;
314+
return -1;
315+
}
316+
#pragma GCC diagnostic pop
317+
318+
// Non-deterministically choose success or failure
319+
int result = __VERIFIER_nondet_int();
320+
321+
if(result == 0)
322+
{
323+
// Success case: fill in the timespec structure with non-deterministic but valid values
324+
time_t sec = __VERIFIER_nondet_time_t();
325+
// Assume reasonable time values (non-negative for typical use cases)
326+
__CPROVER_assume(sec >= 0);
327+
tp->tv_sec = sec;
328+
329+
// Nanoseconds should be between 0 and 999,999,999
330+
long nanosec = __VERIFIER_nondet_long();
331+
__CPROVER_assume(nanosec >= 0 && nanosec <= 999999999L);
332+
tp->tv_nsec = nanosec;
333+
334+
return 0;
335+
}
336+
else
337+
{
338+
// Failure case: set errno and return -1
339+
int error_code = __VERIFIER_nondet_int();
340+
// Most common error codes for clock_gettime
341+
__CPROVER_assume(
342+
error_code == EINVAL || error_code == EACCES || error_code == ENODEV ||
343+
error_code == ENOTSUP || error_code == EOVERFLOW || error_code == EPERM);
344+
errno = error_code;
345+
return -1;
346+
}
347+
}

0 commit comments

Comments
 (0)