Skip to content

Commit 4408204

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 4408204

File tree

3 files changed

+141
-0
lines changed

3 files changed

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