Skip to content

Commit 6eef691

Browse files
committed
More complete modelling of fgets
fgets may (and certainly does when successful) change the contents of the buffer passed in. Building a regression test showed further deficiences in the stdio library model: 1) MacOS uses _fdopen; 2) fclose uses free from stdlib.h.
1 parent d023498 commit 6eef691

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

regression/cbmc/fgets1/main.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <stdio.h>
2+
#include <assert.h>
3+
4+
int main()
5+
{
6+
char buffer[3]={'a', 'b', '\0'};
7+
FILE *f=fdopen(0, "r");
8+
if(!f)
9+
return 1;
10+
11+
char *p=fgets(buffer, 3, f);
12+
if(p)
13+
{
14+
assert(buffer[1]==p[1]);
15+
assert(buffer[2]=='\0');
16+
assert(p[1]=='b'); // expected to fail
17+
}
18+
19+
fclose(f);
20+
21+
return 0;
22+
}

regression/cbmc/fgets1/test.desc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--bounds-check --pointer-check
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
\[main.assertion.3\] assertion p\[1\]=='b': FAILURE
8+
\*\* 1 of 38 failed \(2 iterations\)
9+
--
10+
^warning: ignoring

src/ansi-c/library/stdio.c

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,14 @@ inline FILE *fopen(const char *filename, const char *mode)
6565
FILE *fopen_result;
6666

6767
_Bool fopen_error;
68+
69+
#if !defined(__linux__) || defined(__GLIBC__)
6870
fopen_result=fopen_error?NULL:malloc(sizeof(FILE));
71+
#else
72+
// libraries need to expose the definition of FILE; this is the
73+
// case for musl
74+
fopen_result=fopen_error?NULL:malloc(sizeof(int));
75+
#endif
6976

7077
#ifdef __CPROVER_CUSTOM_BITVECTOR_ANALYSIS
7178
__CPROVER_set_must(fopen_result, "open");
@@ -99,6 +106,11 @@ inline FILE* freopen(const char *filename, const char *mode, FILE *f)
99106
#define __CPROVER_STDIO_H_INCLUDED
100107
#endif
101108

109+
#ifndef __CPROVER_STDLIB_H_INCLUDED
110+
#include <stdlib.h>
111+
#define __CPROVER_STDLIB_H_INCLUDED
112+
#endif
113+
102114
inline int fclose(FILE *stream)
103115
{
104116
__CPROVER_HIDE:;
@@ -135,10 +147,47 @@ inline FILE *fdopen(int handle, const char *mode)
135147
"fdopen zero-termination of 2nd argument");
136148
#endif
137149

150+
#if !defined(__linux__) || defined(__GLIBC__)
151+
FILE *f=malloc(sizeof(FILE));
152+
#else
153+
// libraries need to expose the definition of FILE; this is the
154+
// case for musl
155+
FILE *f=malloc(sizeof(int));
156+
#endif
157+
158+
return f;
159+
}
160+
161+
/* FUNCTION: _fdopen */
162+
163+
// This is for Apple
164+
165+
#ifndef __CPROVER_STDIO_H_INCLUDED
166+
#include <stdio.h>
167+
#define __CPROVER_STDIO_H_INCLUDED
168+
#endif
169+
170+
#ifndef __CPROVER_STDLIB_H_INCLUDED
171+
#include <stdlib.h>
172+
#define __CPROVER_STDLIB_H_INCLUDED
173+
#endif
174+
175+
#ifdef __APPLE__
176+
inline FILE *_fdopen(int handle, const char *mode)
177+
{
178+
__CPROVER_HIDE:;
179+
(void)handle;
180+
(void)*mode;
181+
#ifdef __CPROVER_STRING_ABSTRACTION
182+
__CPROVER_assert(__CPROVER_is_zero_string(mode),
183+
"fdopen zero-termination of 2nd argument");
184+
#endif
185+
138186
FILE *f=malloc(sizeof(FILE));
139187

140188
return f;
141189
}
190+
#endif
142191

143192
/* FUNCTION: fgets */
144193

@@ -147,7 +196,7 @@ inline FILE *fdopen(int handle, const char *mode)
147196
#define __CPROVER_STDIO_H_INCLUDED
148197
#endif
149198

150-
inline char *fgets(char *str, int size, FILE *stream)
199+
char *fgets(char *str, int size, FILE *stream)
151200
{
152201
__CPROVER_HIDE:;
153202
__CPROVER_bool error;
@@ -169,6 +218,16 @@ inline char *fgets(char *str, int size, FILE *stream)
169218
__CPROVER_is_zero_string(str)=!error;
170219
__CPROVER_zero_string_length(str)=resulting_size;
171220
}
221+
#else
222+
if(size>0)
223+
{
224+
int str_length;
225+
__CPROVER_assume(str_length>=0 && str_length<size);
226+
char contents_nondet[str_length];
227+
__CPROVER_array_replace(str, contents_nondet);
228+
if(!error)
229+
str[str_length]='\0';
230+
}
172231
#endif
173232

174233
return error?0:str;

0 commit comments

Comments
 (0)