Skip to content

Commit bc0ebd3

Browse files
committed
Bounds checks in fgets, read
Follow-up to bb8cfaa. Also fixes all types of read, _read, write, _write to match those specified by Visual Studio's documentation. Fixes: #1771
1 parent bcd88a0 commit bc0ebd3

File tree

5 files changed

+76
-23
lines changed

5 files changed

+76
-23
lines changed

regression/cbmc/fgets1/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ main.c
55
^SIGNAL=0$
66
^VERIFICATION FAILED$
77
\[main.assertion.3\] assertion p\[1\]=='b': FAILURE
8-
\*\* 1 of \d+ failed
8+
\*\* 2 of \d+ failed
99
--
1010
^warning: ignoring

regression/cbmc/read1/main.c

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifdef _WIN32
2+
#include <io.h>
3+
#else
4+
#include <unistd.h>
5+
#endif
6+
7+
#include <stdio.h>
8+
9+
int main()
10+
{
11+
char data[20];
12+
13+
if(read(0, data, 100) < 0)
14+
printf("An error has occurred");
15+
else
16+
printf("Read occurred");
17+
18+
return 0;
19+
}

regression/cbmc/read1/test.desc

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--bounds-check --pointer-check --unwind 100
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
\[.*] dereference failure: pointer outside object bounds in .*: FAILURE
8+
\*\* 1 of .* failed \(.*\)
9+
--
10+
^warning: ignoring

src/ansi-c/library/stdio.c

+3
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ char *fgets(char *str, int size, FILE *stream)
247247
{
248248
int str_length=__VERIFIER_nondet_int();
249249
__CPROVER_assume(str_length>=0 && str_length<size);
250+
// check that the memory is accessible
251+
(void)*(char *)str;
252+
(void)*(((const char *)str) + str_length - 1);
250253
char contents_nondet[str_length];
251254
__CPROVER_array_replace(str, contents_nondet);
252255
if(!error)

src/ansi-c/library/unistd.c

+43-22
Original file line numberDiff line numberDiff line change
@@ -122,27 +122,30 @@ inline int _close(int fildes)
122122
// write to _write; this is covered by the explicit definition of
123123
// _write below
124124
#ifdef _MSC_VER
125-
#define ssize_t signed long
125+
#define ret_type int
126+
#define size_type unsigned
126127
#else
127128
#ifndef __CPROVER_SYS_TYPES_H_INCLUDED
128129
#include <sys/types.h>
129130
#define __CPROVER_SYS_TYPES_H_INCLUDED
130131
#endif
132+
#define ret_type ssize_t
133+
#define size_type size_t
131134
#endif
132135

133136
extern struct __CPROVER_pipet __CPROVER_pipes[];
134137
// offset to make sure we don't collide with other fds
135138
extern const int __CPROVER_pipe_offset;
136139

137-
ssize_t __VERIFIER_nondet_ssize_t();
140+
ret_type __VERIFIER_nondet_ret_type();
138141

139-
ssize_t write(int fildes, const void *buf, size_t nbyte)
142+
ret_type write(int fildes, const void *buf, size_type nbyte)
140143
{
141144
__CPROVER_HIDE:;
142145
if((fildes>=0 && fildes<=2) || fildes < __CPROVER_pipe_offset)
143146
{
144-
ssize_t retval=__VERIFIER_nondet_ssize_t();
145-
__CPROVER_assume(retval>=-1 && retval<=(ssize_t)nbyte);
147+
ret_type retval=__VERIFIER_nondet_ret_type();
148+
__CPROVER_assume(retval>=-1 && retval<=(ret_type)nbyte);
146149
return retval;
147150
}
148151

@@ -155,7 +158,7 @@ ssize_t write(int fildes, const void *buf, size_t nbyte)
155158
sizeof(__CPROVER_pipes[fildes].data) >=
156159
__CPROVER_pipes[fildes].next_avail+nbyte)
157160
{
158-
for(size_t i=0; i<nbyte; ++i)
161+
for(size_type i=0; i<nbyte; ++i)
159162
__CPROVER_pipes[fildes].data[i+__CPROVER_pipes[fildes].next_avail]=
160163
((char*)buf)[i];
161164
__CPROVER_pipes[fildes].next_avail+=nbyte;
@@ -168,17 +171,20 @@ ssize_t write(int fildes, const void *buf, size_t nbyte)
168171
/* FUNCTION: _write */
169172

170173
#ifdef _MSC_VER
171-
#define ssize_t signed long
174+
#define ret_type int
175+
#define size_type unsigned
172176
#else
173177
#ifndef __CPROVER_SYS_TYPES_H_INCLUDED
174178
#include <sys/types.h>
175179
#define __CPROVER_SYS_TYPES_H_INCLUDED
176180
#endif
181+
#define ret_type ssize_t
182+
#define size_type size_t
177183
#endif
178184

179-
ssize_t write(int fildes, const void *buf, size_t nbyte);
185+
ret_type write(int fildes, const void *buf, size_type nbyte);
180186

181-
inline ssize_t _write(int fildes, const void *buf, size_t nbyte)
187+
inline ret_type _write(int fildes, const void *buf, size_type nbyte)
182188
{
183189
__CPROVER_HIDE:;
184190
return write(fildes, buf, nbyte);
@@ -190,42 +196,54 @@ inline ssize_t _write(int fildes, const void *buf, size_t nbyte)
190196
// read to _read; this is covered by the explicit definition of _read
191197
// below
192198
#ifdef _MSC_VER
193-
#define ssize_t signed long
199+
#define ret_type int
200+
#define size_type unsigned
194201
#else
195202
#ifndef __CPROVER_SYS_TYPES_H_INCLUDED
196203
#include <sys/types.h>
197204
#define __CPROVER_SYS_TYPES_H_INCLUDED
198205
#endif
206+
#define ret_type ssize_t
207+
#define size_type size_t
199208
#endif
200209

201210
extern struct __CPROVER_pipet __CPROVER_pipes[];
202211
// offset to make sure we don't collide with other fds
203212
extern const int __CPROVER_pipe_offset;
204213

205214
__CPROVER_bool __VERIFIER_nondet___CPROVER_bool();
206-
ssize_t __VERIFIER_nondet_ssize_t();
215+
ret_type __VERIFIER_nondet_ret_type();
216+
size_type __VERIFIER_nondet_size_type();
207217

208-
ssize_t read(int fildes, void *buf, size_t nbyte)
218+
ret_type read(int fildes, void *buf, size_type nbyte)
209219
{
210220
__CPROVER_HIDE:;
211221
if((fildes>=0 && fildes<=2) || fildes < __CPROVER_pipe_offset)
212222
{
213-
ssize_t nread=__VERIFIER_nondet_ssize_t();
214-
__CPROVER_assume(0<=nread && (size_t)nread<=nbyte);
223+
ret_type nread=__VERIFIER_nondet_ret_type();
224+
__CPROVER_assume(0<=nread && (size_type)nread<=nbyte);
215225

226+
__CPROVER_bool error=__VERIFIER_nondet___CPROVER_bool();
216227
#if 0
217-
size_t i;
228+
size_type i;
218229
for(i=0; i<nbyte; i++)
219230
{
220231
char nondet_char;
221232
((char *)buf)[i]=nondet_char;
222233
}
223234
#else
224-
char nondet_bytes[nbyte];
225-
__CPROVER_array_replace((char*)buf, nondet_bytes);
235+
if(nbyte>0)
236+
{
237+
size_type str_length=__VERIFIER_nondet_size_type();
238+
__CPROVER_assume(error ? str_length<=nbyte : str_length==nbyte);
239+
// check that the memory is accessible
240+
(void)*(char *)buf;
241+
(void)*(((const char *)buf) + str_length - 1);
242+
char contents_nondet[str_length];
243+
__CPROVER_array_replace((char*)buf, contents_nondet);
244+
}
226245
#endif
227246

228-
__CPROVER_bool error=__VERIFIER_nondet___CPROVER_bool();
229247
return error ? -1 : nread;
230248
}
231249

@@ -236,7 +254,7 @@ ssize_t read(int fildes, void *buf, size_t nbyte)
236254
__CPROVER_atomic_begin();
237255
if(!__CPROVER_pipes[fildes].widowed)
238256
{
239-
for(size_t i=0; i<nbyte &&
257+
for(size_type i=0; i<nbyte &&
240258
__CPROVER_pipes[fildes].next_unread <
241259
__CPROVER_pipes[fildes].next_avail;
242260
++i)
@@ -257,17 +275,20 @@ ssize_t read(int fildes, void *buf, size_t nbyte)
257275
/* FUNCTION: _read */
258276

259277
#ifdef _MSC_VER
260-
#define ssize_t signed long
278+
#define ret_type int
279+
#define size_type unsigned
261280
#else
262281
#ifndef __CPROVER_SYS_TYPES_H_INCLUDED
263282
#include <sys/types.h>
264283
#define __CPROVER_SYS_TYPES_H_INCLUDED
265284
#endif
285+
#define ret_type ssize_t
286+
#define size_type size_t
266287
#endif
267288

268-
ssize_t read(int fildes, void *buf, size_t nbyte);
289+
ret_type read(int fildes, void *buf, size_type nbyte);
269290

270-
inline ssize_t _read(int fildes, void *buf, size_t nbyte)
291+
inline ret_type _read(int fildes, void *buf, size_type nbyte)
271292
{
272293
__CPROVER_HIDE:;
273294
return read(fildes, buf, nbyte);

0 commit comments

Comments
 (0)