Skip to content

Commit 85ca3e5

Browse files
committed
C library: Refine and improve stdio models
Fixes portability to FreeBSD, which redefines several functions as macros that would only conditionally call that function. Also, ensure that stdin/stdout/stderr point to valid objects when those are fdopen'ed.
1 parent 3a1272a commit 85ca3e5

File tree

4 files changed

+132
-24
lines changed
  • .github/workflows
  • regression
    • cbmc-library/fileno-01
    • contracts-dfcc/variant_multidimensional_ackermann
  • src/ansi-c/library

4 files changed

+132
-24
lines changed

.github/workflows/bsd.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
# gmake TAGS='[!shouldfail]' -C jbmc/unit test
6363
echo "Run regression tests"
6464
gmake -C regression/cbmc test
65+
gmake -C regression/cbmc-library test
6566
# gmake -C regression test-parallel JOBS=2
6667
# gmake -C regression/cbmc test-paths-lifo
6768
# env PATH=$PATH:`pwd`/src/solvers gmake -C regression/cbmc test-cprover-smt2
@@ -123,6 +124,10 @@ jobs:
123124
# gmake TAGS='[!shouldfail]' -C jbmc/unit test
124125
echo "Run regression tests"
125126
gmake -C regression/cbmc test
127+
# TODO: fileno and *fprintf tests are failing, requires debugging
128+
# https://github.com/openbsd/src/blob/master/include/stdio.h may be
129+
# useful (likely need to allocate __sF)
130+
gmake -C regression/cbmc-library test || true
126131
# gmake -C regression test-parallel JOBS=2
127132
# gmake -C regression/cbmc test-paths-lifo
128133
# env PATH=$PATH:`pwd`/src/solvers gmake -C regression/cbmc test-cprover-smt2
@@ -187,6 +192,7 @@ jobs:
187192
echo "Run regression tests"
188193
# TODO: we need to model some more library functions
189194
gmake -C regression/cbmc test || true
195+
gmake -C regression/cbmc-library test || true
190196
# gmake -C regression test-parallel JOBS=2
191197
# gmake -C regression/cbmc test-paths-lifo
192198
# env PATH=$PATH:`pwd`/src/solvers gmake -C regression/cbmc test-cprover-smt2

regression/cbmc-library/fileno-01/main.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,10 @@
33

44
int main()
55
{
6-
// requires initialization of stdin/stdout/stderr
7-
// assert(fileno(stdin) == 0);
8-
// assert(fileno(stdout) == 1);
9-
// assert(fileno(stderr) == 2);
10-
116
int fd;
127
FILE *some_file = fdopen(fd, "");
13-
assert(fileno(some_file) >= -1);
8+
if(some_file)
9+
assert(fileno(some_file) >= -1);
1410

1511
return 0;
1612
}

regression/contracts-dfcc/variant_multidimensional_ackermann/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ int main()
88
int n = 5;
99
int result = ackermann(m, n);
1010

11-
printf("Result of the Ackermann function: %d\n", result);
11+
// we don't currently have contracts on what printf is assigning to
12+
// printf("Result of the Ackermann function: %d\n", result);
1213
return 0;
1314
}
1415

src/ansi-c/library/stdio.c

Lines changed: 122 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,7 @@
66
#define __CPROVER_STDIO_H_INCLUDED
77
#endif
88

9-
/* undefine macros in OpenBSD's stdio.h that are problematic to the checker. */
10-
#if defined(__OpenBSD__)
11-
#undef getchar
129
#undef putchar
13-
#undef getc
14-
#undef feof
15-
#undef ferror
16-
#undef fileno
17-
#endif
1810

1911
__CPROVER_bool __VERIFIER_nondet___CPROVER_bool(void);
2012

@@ -197,7 +189,8 @@ __CPROVER_HIDE:;
197189
__CPROVER_set_must(stream, "closed");
198190
#endif
199191
int return_value=__VERIFIER_nondet_int();
200-
free(stream);
192+
if(stream != stdin && stream != stdout && stream != stderr)
193+
free(stream);
201194
return return_value;
202195
}
203196

@@ -213,25 +206,83 @@ __CPROVER_HIDE:;
213206
#define __CPROVER_STDLIB_H_INCLUDED
214207
#endif
215208

209+
#ifndef __CPROVER_ERRNO_H_INCLUDED
210+
# include <errno.h>
211+
# define __CPROVER_ERRNO_H_INCLUDED
212+
#endif
213+
216214
FILE *fdopen(int handle, const char *mode)
217215
{
218216
__CPROVER_HIDE:;
219-
(void)handle;
217+
if(handle < 0)
218+
{
219+
errno = EBADF;
220+
return NULL;
221+
}
220222
(void)*mode;
221223
#ifdef __CPROVER_STRING_ABSTRACTION
222224
__CPROVER_assert(__CPROVER_is_zero_string(mode),
223225
"fdopen zero-termination of 2nd argument");
224226
#endif
225227

226-
#if !defined(__linux__) || defined(__GLIBC__)
227-
FILE *f=malloc(sizeof(FILE));
228+
#if defined(_WIN32) || defined(__OpenBSD__) || defined(__NetBSD__)
229+
switch(handle)
230+
{
231+
case 0:
232+
return stdin;
233+
case 1:
234+
return stdout;
235+
case 2:
236+
return stderr;
237+
default:
238+
{
239+
FILE *f = malloc(sizeof(FILE));
240+
__CPROVER_assume(fileno(f) == handle);
241+
return f;
242+
}
243+
}
228244
#else
229-
// libraries need to expose the definition of FILE; this is the
245+
# if !defined(__linux__) || defined(__GLIBC__)
246+
static FILE stdin_file;
247+
static FILE stdout_file;
248+
static FILE stderr_file;
249+
# else
250+
// libraries need not expose the definition of FILE; this is the
230251
// case for musl
231-
FILE *f=malloc(sizeof(int));
232-
#endif
252+
static int stdin_file;
253+
static int stdout_file;
254+
static int stderr_file;
255+
# endif
256+
257+
FILE *f = NULL;
258+
switch(handle)
259+
{
260+
case 0:
261+
stdin = &stdin_file;
262+
__CPROVER_havoc_object(&stdin_file);
263+
f = &stdin_file;
264+
break;
265+
case 1:
266+
stdout = &stdout_file;
267+
__CPROVER_havoc_object(&stdout_file);
268+
f = &stdout_file;
269+
break;
270+
case 2:
271+
stderr = &stderr_file;
272+
__CPROVER_havoc_object(&stderr_file);
273+
f = &stderr_file;
274+
break;
275+
default:
276+
# if !defined(__linux__) || defined(__GLIBC__)
277+
f = malloc(sizeof(FILE));
278+
# else
279+
f = malloc(sizeof(int));
280+
# endif
281+
}
233282

283+
__CPROVER_assume(fileno(f) == handle);
234284
return f;
285+
#endif
235286
}
236287

237288
/* FUNCTION: _fdopen */
@@ -251,19 +302,60 @@ FILE *fdopen(int handle, const char *mode)
251302
#define __CPROVER_STDLIB_H_INCLUDED
252303
#endif
253304

305+
#ifndef __CPROVER_ERRNO_H_INCLUDED
306+
# include <errno.h>
307+
# define __CPROVER_ERRNO_H_INCLUDED
308+
#endif
309+
254310
#ifdef __APPLE__
311+
312+
# ifndef LIBRARY_CHECK
313+
FILE *stdin;
314+
FILE *stdout;
315+
FILE *stderr;
316+
# endif
317+
255318
FILE *_fdopen(int handle, const char *mode)
256319
{
257320
__CPROVER_HIDE:;
258-
(void)handle;
321+
if(handle < 0)
322+
{
323+
errno = EBADF;
324+
return NULL;
325+
}
259326
(void)*mode;
260327
#ifdef __CPROVER_STRING_ABSTRACTION
261328
__CPROVER_assert(__CPROVER_is_zero_string(mode),
262329
"fdopen zero-termination of 2nd argument");
263330
#endif
264331

265-
FILE *f=malloc(sizeof(FILE));
332+
static FILE stdin_file;
333+
static FILE stdout_file;
334+
static FILE stderr_file;
266335

336+
FILE *f = NULL;
337+
switch(handle)
338+
{
339+
case 0:
340+
stdin = &stdin_file;
341+
__CPROVER_havoc_object(&stdin_file);
342+
f = &stdin_file;
343+
break;
344+
case 1:
345+
stdout = &stdout_file;
346+
__CPROVER_havoc_object(&stdout_file);
347+
f = &stdout_file;
348+
break;
349+
case 2:
350+
stderr = &stderr_file;
351+
__CPROVER_havoc_object(&stderr_file);
352+
f = &stderr_file;
353+
break;
354+
default:
355+
f = malloc(sizeof(FILE));
356+
}
357+
358+
__CPROVER_assume(fileno(f) == handle);
267359
return f;
268360
}
269361
#endif
@@ -466,6 +558,8 @@ __CPROVER_HIDE:;
466558
#define __CPROVER_STDIO_H_INCLUDED
467559
#endif
468560

561+
#undef feof
562+
469563
int __VERIFIER_nondet_int(void);
470564

471565
int feof(FILE *stream)
@@ -498,6 +592,8 @@ int feof(FILE *stream)
498592
#define __CPROVER_STDIO_H_INCLUDED
499593
#endif
500594

595+
#undef ferror
596+
501597
int __VERIFIER_nondet_int(void);
502598

503599
int ferror(FILE *stream)
@@ -530,6 +626,8 @@ int ferror(FILE *stream)
530626
#define __CPROVER_STDIO_H_INCLUDED
531627
#endif
532628

629+
#undef fileno
630+
533631
int __VERIFIER_nondet_int(void);
534632

535633
int fileno(FILE *stream)
@@ -695,6 +793,8 @@ int fgetc(FILE *stream)
695793
#define __CPROVER_STDIO_H_INCLUDED
696794
#endif
697795

796+
#undef getc
797+
698798
int __VERIFIER_nondet_int(void);
699799

700800
int getc(FILE *stream)
@@ -731,6 +831,8 @@ int getc(FILE *stream)
731831
#define __CPROVER_STDIO_H_INCLUDED
732832
#endif
733833

834+
#undef getchar
835+
734836
int __VERIFIER_nondet_int(void);
735837

736838
int getchar(void)
@@ -1602,10 +1704,13 @@ FILE *__acrt_iob_func(unsigned fd)
16021704
switch(fd)
16031705
{
16041706
case 0:
1707+
__CPROVER_havoc_object(&stdin_file);
16051708
return &stdin_file;
16061709
case 1:
1710+
__CPROVER_havoc_object(&stdout_file);
16071711
return &stdout_file;
16081712
case 2:
1713+
__CPROVER_havoc_object(&stderr_file);
16091714
return &stderr_file;
16101715
default:
16111716
return (FILE *)0;

0 commit comments

Comments
 (0)