Skip to content

Commit 48edefe

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 56d8598 commit 48edefe

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <assert.h>
2+
#include <errno.h>
3+
#include <stdint.h>
4+
#include <time.h>
5+
6+
// Test function from the issue description
7+
uint32_t my_time(void)
8+
{
9+
struct timespec t;
10+
int ret = clock_gettime(CLOCK_MONOTONIC, &t);
11+
12+
// If clock_gettime fails, return a safe value
13+
if(ret != 0)
14+
{
15+
return 0;
16+
}
17+
18+
return (t.tv_nsec / 1000000) + ((t.tv_sec % 86400) * 1000);
19+
}
20+
21+
int main()
22+
{
23+
// Test null pointer behavior
24+
int result_null = clock_gettime(CLOCK_REALTIME, 0);
25+
if(result_null == -1)
26+
{
27+
// errno should be set to EFAULT for null pointer
28+
assert(errno == EFAULT);
29+
}
30+
31+
struct timespec ts;
32+
33+
// Test basic functionality with different clock types
34+
int result1 = clock_gettime(CLOCK_REALTIME, &ts);
35+
assert(result1 == 0 || result1 == -1);
36+
37+
if(result1 == 0)
38+
{
39+
// If successful, tv_nsec should be in valid range
40+
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999L);
41+
}
42+
43+
// Test with CLOCK_MONOTONIC
44+
int result2 = clock_gettime(CLOCK_MONOTONIC, &ts);
45+
assert(result2 == 0 || result2 == -1);
46+
47+
if(result2 == 0)
48+
{
49+
// If successful, tv_nsec should be in valid range
50+
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999L);
51+
}
52+
53+
// Test the my_time function from the issue
54+
// Note: my_time() handles the failure case internally
55+
uint32_t time_result = my_time();
56+
// Should return either 0 (on failure) or valid millisecond value within a day
57+
assert(time_result < 86400000 || time_result == 0);
58+
59+
return 0;
60+
}
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: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,64 @@ __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+
if(!tp)
309+
{
310+
errno = EFAULT;
311+
return -1;
312+
}
313+
314+
// Non-deterministically choose success or failure
315+
int result = __VERIFIER_nondet_int();
316+
317+
if(result == 0)
318+
{
319+
// Success case: fill in the timespec structure with non-deterministic but valid values
320+
time_t sec = __VERIFIER_nondet_time_t();
321+
// Assume reasonable time values (non-negative for typical use cases)
322+
__CPROVER_assume(sec >= 0);
323+
tp->tv_sec = sec;
324+
325+
// Nanoseconds should be between 0 and 999,999,999
326+
long nanosec = __VERIFIER_nondet_long();
327+
__CPROVER_assume(nanosec >= 0 && nanosec <= 999999999L);
328+
tp->tv_nsec = nanosec;
329+
330+
return 0;
331+
}
332+
else
333+
{
334+
// Failure case: set errno and return -1
335+
int error_code = __VERIFIER_nondet_int();
336+
// Most common error codes for clock_gettime
337+
__CPROVER_assume(
338+
error_code == EINVAL || error_code == EACCES || error_code == ENODEV ||
339+
error_code == ENOTSUP || error_code == EOVERFLOW || error_code == EPERM);
340+
errno = error_code;
341+
return -1;
342+
}
343+
}

0 commit comments

Comments
 (0)