Skip to content

Commit 839387a

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 839387a

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
#ifdef _WIN32
8+
# define CLOCK_MONOTONIC 1
9+
# define CLOCK_REALTIME 1
10+
int clock_gettime(int clockid, struct timespec *tp);
11+
#endif
12+
13+
// Test function from the issue description
14+
uint32_t my_time(void)
15+
{
16+
struct timespec t;
17+
int ret = clock_gettime(CLOCK_MONOTONIC, &t);
18+
19+
// If clock_gettime fails, return a safe value
20+
if(ret != 0)
21+
{
22+
return 0;
23+
}
24+
25+
return (t.tv_nsec / 1000000) + ((t.tv_sec % 86400) * 1000);
26+
}
27+
28+
int main()
29+
{
30+
// Test null pointer behavior
31+
int result_null = clock_gettime(CLOCK_REALTIME, 0);
32+
if(result_null == -1)
33+
{
34+
// errno should be set to EFAULT for null pointer
35+
assert(errno == EFAULT);
36+
}
37+
38+
struct timespec ts;
39+
40+
// Test basic functionality with different clock types
41+
int result1 = clock_gettime(CLOCK_REALTIME, &ts);
42+
assert(result1 == 0 || result1 == -1);
43+
44+
if(result1 == 0)
45+
{
46+
// If successful, tv_nsec should be in valid range
47+
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999L);
48+
}
49+
50+
// Test with CLOCK_MONOTONIC
51+
int result2 = clock_gettime(CLOCK_MONOTONIC, &ts);
52+
assert(result2 == 0 || result2 == -1);
53+
54+
if(result2 == 0)
55+
{
56+
// If successful, tv_nsec should be in valid range
57+
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999L);
58+
}
59+
60+
// Test the my_time function from the issue
61+
// Note: my_time() handles the failure case internally
62+
uint32_t time_result = my_time();
63+
// Should return either 0 (on failure) or valid millisecond value within a day
64+
assert(time_result < 86400000 || time_result == 0);
65+
66+
return 0;
67+
}
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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,72 @@ __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+
#ifdef _WIN32
301+
typedef int clockid_t;
302+
#endif
303+
304+
int clock_gettime(clockid_t clockid, struct timespec *tp)
305+
{
306+
__CPROVER_HIDE:;
307+
308+
// Use the clockid parameter (all clock types are modeled the same way)
309+
(void)clockid;
310+
311+
// Check for null pointer - should set errno to EFAULT
312+
// Some systems have C headers where `tp` is annotated to be nonnull
313+
#pragma GCC diagnostic push
314+
#pragma GCC diagnostic ignored "-Wnonnull-compare"
315+
if(!tp)
316+
{
317+
errno = EFAULT;
318+
return -1;
319+
}
320+
#pragma GCC diagnostic pop
321+
322+
// Non-deterministically choose success or failure
323+
int result = __VERIFIER_nondet_int();
324+
325+
if(result == 0)
326+
{
327+
// Success case: fill in the timespec structure with non-deterministic but valid values
328+
time_t sec = __VERIFIER_nondet_time_t();
329+
// Assume reasonable time values (non-negative for typical use cases)
330+
__CPROVER_assume(sec >= 0);
331+
tp->tv_sec = sec;
332+
333+
// Nanoseconds should be between 0 and 999,999,999
334+
long nanosec = __VERIFIER_nondet_long();
335+
__CPROVER_assume(nanosec >= 0 && nanosec <= 999999999L);
336+
tp->tv_nsec = nanosec;
337+
338+
return 0;
339+
}
340+
else
341+
{
342+
// Failure case: set errno and return -1
343+
int error_code = __VERIFIER_nondet_int();
344+
// Most common error codes for clock_gettime
345+
__CPROVER_assume(
346+
error_code == EINVAL || error_code == EACCES || error_code == ENODEV ||
347+
error_code == ENOTSUP || error_code == EOVERFLOW || error_code == EPERM);
348+
errno = error_code;
349+
return -1;
350+
}
351+
}

0 commit comments

Comments
 (0)