Skip to content

Commit 3d5bb00

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 50be009 commit 3d5bb00

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
@@ -63,6 +63,7 @@ jobs:
6363
# gmake TAGS='[!shouldfail]' -C jbmc/unit test
6464
echo "Run regression tests"
6565
gmake -C regression/cbmc test
66+
gmake -C regression/cbmc-library test
6667
# gmake -C regression test-parallel JOBS=2
6768
# gmake -C regression/cbmc test-paths-lifo
6869
# env PATH=$PATH:`pwd`/src/solvers gmake -C regression/cbmc test-cprover-smt2
@@ -125,6 +126,10 @@ jobs:
125126
# gmake TAGS='[!shouldfail]' -C jbmc/unit test
126127
echo "Run regression tests"
127128
gmake -C regression/cbmc test
129+
# TODO: fileno and *fprintf tests are failing, requires debugging
130+
# https://github.com/openbsd/src/blob/master/include/stdio.h may be
131+
# useful (likely need to allocate __sF)
132+
gmake -C regression/cbmc-library test || true
128133
# gmake -C regression test-parallel JOBS=2
129134
# gmake -C regression/cbmc test-paths-lifo
130135
# env PATH=$PATH:`pwd`/src/solvers gmake -C regression/cbmc test-cprover-smt2
@@ -190,6 +195,7 @@ jobs:
190195
echo "Run regression tests"
191196
# TODO: we need to model some more library functions
192197
gmake -C regression/cbmc test || true
198+
gmake -C regression/cbmc-library test || true
193199
# gmake -C regression test-parallel JOBS=2
194200
# gmake -C regression/cbmc test-paths-lifo
195201
# 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

@@ -237,7 +229,8 @@ __CPROVER_HIDE:;
237229
__CPROVER_set_must(stream, "closed");
238230
#endif
239231
int return_value=__VERIFIER_nondet_int();
240-
free(stream);
232+
if(stream != stdin && stream != stdout && stream != stderr)
233+
free(stream);
241234
return return_value;
242235
}
243236

@@ -253,25 +246,83 @@ __CPROVER_HIDE:;
253246
#define __CPROVER_STDLIB_H_INCLUDED
254247
#endif
255248

249+
#ifndef __CPROVER_ERRNO_H_INCLUDED
250+
# include <errno.h>
251+
# define __CPROVER_ERRNO_H_INCLUDED
252+
#endif
253+
256254
FILE *fdopen(int handle, const char *mode)
257255
{
258256
__CPROVER_HIDE:;
259-
(void)handle;
257+
if(handle < 0)
258+
{
259+
errno = EBADF;
260+
return NULL;
261+
}
260262
(void)*mode;
261263
#ifdef __CPROVER_STRING_ABSTRACTION
262264
__CPROVER_assert(__CPROVER_is_zero_string(mode),
263265
"fdopen zero-termination of 2nd argument");
264266
#endif
265267

266-
#if !defined(__linux__) || defined(__GLIBC__)
267-
FILE *f=malloc(sizeof(FILE));
268+
#if defined(_WIN32) || defined(__OpenBSD__) || defined(__NetBSD__)
269+
switch(handle)
270+
{
271+
case 0:
272+
return stdin;
273+
case 1:
274+
return stdout;
275+
case 2:
276+
return stderr;
277+
default:
278+
{
279+
FILE *f = malloc(sizeof(FILE));
280+
__CPROVER_assume(fileno(f) == handle);
281+
return f;
282+
}
283+
}
268284
#else
269-
// libraries need to expose the definition of FILE; this is the
285+
# if !defined(__linux__) || defined(__GLIBC__)
286+
static FILE stdin_file;
287+
static FILE stdout_file;
288+
static FILE stderr_file;
289+
# else
290+
// libraries need not expose the definition of FILE; this is the
270291
// case for musl
271-
FILE *f=malloc(sizeof(int));
272-
#endif
292+
static int stdin_file;
293+
static int stdout_file;
294+
static int stderr_file;
295+
# endif
296+
297+
FILE *f = NULL;
298+
switch(handle)
299+
{
300+
case 0:
301+
stdin = &stdin_file;
302+
__CPROVER_havoc_object(&stdin_file);
303+
f = &stdin_file;
304+
break;
305+
case 1:
306+
stdout = &stdout_file;
307+
__CPROVER_havoc_object(&stdout_file);
308+
f = &stdout_file;
309+
break;
310+
case 2:
311+
stderr = &stderr_file;
312+
__CPROVER_havoc_object(&stderr_file);
313+
f = &stderr_file;
314+
break;
315+
default:
316+
# if !defined(__linux__) || defined(__GLIBC__)
317+
f = malloc(sizeof(FILE));
318+
# else
319+
f = malloc(sizeof(int));
320+
# endif
321+
}
273322

323+
__CPROVER_assume(fileno(f) == handle);
274324
return f;
325+
#endif
275326
}
276327

277328
/* FUNCTION: _fdopen */
@@ -291,19 +342,60 @@ FILE *fdopen(int handle, const char *mode)
291342
#define __CPROVER_STDLIB_H_INCLUDED
292343
#endif
293344

345+
#ifndef __CPROVER_ERRNO_H_INCLUDED
346+
# include <errno.h>
347+
# define __CPROVER_ERRNO_H_INCLUDED
348+
#endif
349+
294350
#ifdef __APPLE__
351+
352+
# ifndef LIBRARY_CHECK
353+
FILE *stdin;
354+
FILE *stdout;
355+
FILE *stderr;
356+
# endif
357+
295358
FILE *_fdopen(int handle, const char *mode)
296359
{
297360
__CPROVER_HIDE:;
298-
(void)handle;
361+
if(handle < 0)
362+
{
363+
errno = EBADF;
364+
return NULL;
365+
}
299366
(void)*mode;
300367
#ifdef __CPROVER_STRING_ABSTRACTION
301368
__CPROVER_assert(__CPROVER_is_zero_string(mode),
302369
"fdopen zero-termination of 2nd argument");
303370
#endif
304371

305-
FILE *f=malloc(sizeof(FILE));
372+
static FILE stdin_file;
373+
static FILE stdout_file;
374+
static FILE stderr_file;
375+
376+
FILE *f = NULL;
377+
switch(handle)
378+
{
379+
case 0:
380+
stdin = &stdin_file;
381+
__CPROVER_havoc_object(&stdin_file);
382+
f = &stdin_file;
383+
break;
384+
case 1:
385+
stdout = &stdout_file;
386+
__CPROVER_havoc_object(&stdout_file);
387+
f = &stdout_file;
388+
break;
389+
case 2:
390+
stderr = &stderr_file;
391+
__CPROVER_havoc_object(&stderr_file);
392+
f = &stderr_file;
393+
break;
394+
default:
395+
f = malloc(sizeof(FILE));
396+
}
306397

398+
__CPROVER_assume(fileno(f) == handle);
307399
return f;
308400
}
309401
#endif
@@ -506,6 +598,8 @@ __CPROVER_HIDE:;
506598
#define __CPROVER_STDIO_H_INCLUDED
507599
#endif
508600

601+
#undef feof
602+
509603
int __VERIFIER_nondet_int(void);
510604

511605
int feof(FILE *stream)
@@ -538,6 +632,8 @@ int feof(FILE *stream)
538632
#define __CPROVER_STDIO_H_INCLUDED
539633
#endif
540634

635+
#undef ferror
636+
541637
int __VERIFIER_nondet_int(void);
542638

543639
int ferror(FILE *stream)
@@ -570,6 +666,8 @@ int ferror(FILE *stream)
570666
#define __CPROVER_STDIO_H_INCLUDED
571667
#endif
572668

669+
#undef fileno
670+
573671
int __VERIFIER_nondet_int(void);
574672

575673
int fileno(FILE *stream)
@@ -735,6 +833,8 @@ int fgetc(FILE *stream)
735833
#define __CPROVER_STDIO_H_INCLUDED
736834
#endif
737835

836+
#undef getc
837+
738838
int __VERIFIER_nondet_int(void);
739839

740840
int getc(FILE *stream)
@@ -771,6 +871,8 @@ int getc(FILE *stream)
771871
#define __CPROVER_STDIO_H_INCLUDED
772872
#endif
773873

874+
#undef getchar
875+
774876
int __VERIFIER_nondet_int(void);
775877

776878
int getchar(void)
@@ -1939,10 +2041,13 @@ FILE *__acrt_iob_func(unsigned fd)
19392041
switch(fd)
19402042
{
19412043
case 0:
2044+
__CPROVER_havoc_object(&stdin_file);
19422045
return &stdin_file;
19432046
case 1:
2047+
__CPROVER_havoc_object(&stdout_file);
19442048
return &stdout_file;
19452049
case 2:
2050+
__CPROVER_havoc_object(&stderr_file);
19462051
return &stderr_file;
19472052
default:
19482053
return (FILE *)0;

0 commit comments

Comments
 (0)