diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 25b81ff83abfcef7c40a2e6de049149cb7fdbc53..daa84f4342a604432310f38fed6cfe28a467cc15 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -236,6 +236,7 @@ share/libc/ctype.h: CEA_LGPL share/libc/dirent.h: CEA_LGPL share/libc/dlfcn.h: CEA_LGPL share/libc/endian.h: CEA_LGPL +share/libc/err.h: CEA_LGPL share/libc/errno.c: CEA_LGPL share/libc/errno.h: CEA_LGPL share/libc/fcntl.h: CEA_LGPL diff --git a/share/libc/__fc_libc.h b/share/libc/__fc_libc.h index 2f78bbc8bb1d848eeccbe2192d7343b117bee043..4c0bb5e0ce86dc30dd75508c9eca93c06370a356 100644 --- a/share/libc/__fc_libc.h +++ b/share/libc/__fc_libc.h @@ -37,6 +37,7 @@ #include "dirent.h" #include "dlfcn.h" #include "endian.h" +#include "err.h" #include "errno.h" #include "fcntl.h" #include "features.h" diff --git a/share/libc/argz.c b/share/libc/argz.c index 51f5110052118cafdbe34218164f11870299c802..04744ce2435b680d15d105db6877d0e7ae96ce92 100644 --- a/share/libc/argz.c +++ b/share/libc/argz.c @@ -60,7 +60,7 @@ static void str_append (char **to, size_t *to_len, const char *buf, error_t argz_replace (char **argz, size_t *argz_len, const char *str, const char *with, unsigned *replace_count) { - error_t err = 0; + error_t er = 0; if (str && *str) { char *arg = 0; @@ -71,7 +71,7 @@ error_t argz_replace (char **argz, size_t *argz_len, const char *str, int delayed_copy = 1; size_t str_len = strlen (str), with_len = strlen (with); - while (!err && (arg = argz_next (src, src_len, arg))) { + while (!er && (arg = argz_next (src, src_len, arg))) { char *match = strstr (arg, str); if (match) { char *from = match + str_len; @@ -95,22 +95,22 @@ error_t argz_replace (char **argz, size_t *argz_len, const char *str, if (to) { if (delayed_copy) { if (arg > src) - err = argz_append (&dst, &dst_len, src, (arg - src)); + er = argz_append (&dst, &dst_len, src, (arg - src)); delayed_copy = 0; } - if (! err) - err = argz_add (&dst, &dst_len, to); + if (! er) + er = argz_add (&dst, &dst_len, to); free (to); } else - err = ENOMEM; + er = ENOMEM; if (replace_count) (*replace_count)++; } else if (! delayed_copy) - err = argz_add (&dst, &dst_len, arg); + er = argz_add (&dst, &dst_len, arg); } - if (! err) { + if (! er) { if (! delayed_copy) { free (src); *argz = dst; @@ -120,7 +120,7 @@ error_t argz_replace (char **argz, size_t *argz_len, const char *str, free (dst); } - return err; + return er; } char * argz_next (const char *argz, size_t argz_len, const char *entry) { diff --git a/share/libc/err.h b/share/libc/err.h new file mode 100644 index 0000000000000000000000000000000000000000..e2da20ac48182c5568126ab412a67a9ff4d4e63e --- /dev/null +++ b/share/libc/err.h @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* This file is part of Frama-C. */ +/* */ +/* Copyright (C) 2007-2021 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/**************************************************************************/ + +// Non-POSIX and not in the C ISO standard; this file is defined in the +// GNU libc. + +#ifndef __FC_ERR_H +#define __FC_ERR_H + +#include "features.h" +__PUSH_FC_STDLIB + +#include "__fc_string_axiomatic.h" +#include "stdarg.h" + +__BEGIN_DECLS + +// TODO: extend Variadic to handle these functions, or provide C stubs. +// Currently, given their limited usage and non-standard status, this +// lightweight approach seems better suited. + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \exit_status \from eval; + ensures never_terminates: \false; + */ +void err(int eval, const char *fmt, ...); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \exit_status \from eval; + ensures never_terminates: \false; + */ +void errx(int eval, const char *fmt, ...); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \nothing; + */ +void warn(const char *fmt, ...); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \nothing; + */ +void warnx(const char *fmt, ...); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \exit_status \from eval; + ensures never_terminates: \false; + */ +void verr(int eval, const char *fmt, va_list args); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \exit_status \from eval; + ensures never_terminates: \false; + */ +void verrx(int eval, const char *fmt, va_list args); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \nothing; + */ +void vwarn(const char *fmt, va_list args); + +/*@ + requires fmt_valid_read_or_null: valid_read_string(fmt) || fmt == \null; + assigns \nothing; + */ +void vwarnx(const char *fmt, va_list args); + +__END_DECLS + +__POP_FC_STDLIB +#endif diff --git a/tests/libc/err_h.c b/tests/libc/err_h.c new file mode 100644 index 0000000000000000000000000000000000000000..660e350ab1c0ecaa0f00f381e1a8fd0ee19dbceb --- /dev/null +++ b/tests/libc/err_h.c @@ -0,0 +1,65 @@ +/* run.config + STDOPT: +*/ +#include <err.h> +#include <stdarg.h> + +volatile int nondet; + +void vaw(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); +} + +void vawx(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} + +void vae(int eval, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + verr(eval, fmt, ap); + va_end(ap); +} + +void vaex(int eval, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + verrx(eval, fmt, ap); + va_end(ap); +} + +int main() { + int a = 1, *p = 2; + warn(0); + warn(0, a); + warnx("%d", a); + vaw("%s", "warn"); + vawx(0); + if (nondet) { + err(0, 0); + //@ assert unreachable: \false; + } + if (nondet) { + err(1, 0, a); + //@ assert unreachable: \false; + } + if (nondet) { + errx(2, "%d", a); + //@ assert unreachable: \false; + } + if (nondet) { + vae(0, 0); + //@ assert unreachable: \false; + } + if (nondet) { + vaex(-1, "nogood %d", a); + //@ assert unreachable: \false; + } + return 0; +} diff --git a/tests/libc/fc_libc.c b/tests/libc/fc_libc.c index c122ff2268ffa3f05bfb7a6677e4ca81e7b84ea2..3a04272a5d77ec26a2126907dcb0406017db67a3 100644 --- a/tests/libc/fc_libc.c +++ b/tests/libc/fc_libc.c @@ -37,6 +37,7 @@ #include "dirent.h" #include "dlfcn.h" #include "endian.h" +#include "err.h" #include "errno.h" #include "__fc_alloc_axiomatic.h" #include "__fc_builtin.h" diff --git a/tests/libc/oracle/argz_c.res.oracle b/tests/libc/oracle/argz_c.res.oracle index 2ab7a5fe98c630ecf0df81873e164a57a4e0306d..c423da43955093adff4e4b43d14b9e8a0ca50052 100644 --- a/tests/libc/oracle/argz_c.res.oracle +++ b/tests/libc/oracle/argz_c.res.oracle @@ -1194,7 +1194,7 @@ [eva:final-states] Values at end of function argz_replace: __fc_errno ∈ [--..--] __fc_heap_status ∈ [--..--] - err ∈ {0; 12} + er ∈ {0; 12} argz ∈ {{ &__realloc_argz_append_l271_1[0] ; &__realloc_argz_append_l271_7[0] }} diff --git a/tests/libc/oracle/err_h.res.oracle b/tests/libc/oracle/err_h.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..4c98b5d8c8a1783683870e157b74bc052e4ff502 --- /dev/null +++ b/tests/libc/oracle/err_h.res.oracle @@ -0,0 +1,110 @@ +[kernel] Parsing err_h.c (with preprocessing) +[eva] Analyzing a complete application starting at main +[eva] Computing initial state +[eva] Initial state computed +[eva:initial-state] Values of globals at initialization + nondet ∈ [--..--] +[eva] computing for function warn <- main. + Called from err_h.c:39. +[eva] using specification for function warn +[eva] err_h.c:39: + function warn: precondition 'fmt_valid_read_or_null' got status valid. +[eva] Done for function warn +[eva] computing for function warn <- main. + Called from err_h.c:40. +[eva] err_h.c:40: + function warn: precondition 'fmt_valid_read_or_null' got status valid. +[eva] Done for function warn +[eva] computing for function warnx <- main. + Called from err_h.c:41. +[eva] using specification for function warnx +[eva] err_h.c:41: + function warnx: precondition 'fmt_valid_read_or_null' got status valid. +[eva] Done for function warnx +[eva] computing for function vaw <- main. + Called from err_h.c:42. +[eva] computing for function vwarn <- vaw <- main. + Called from err_h.c:12. +[eva] using specification for function vwarn +[eva] err_h.c:12: + function vwarn: precondition 'fmt_valid_read_or_null' got status valid. +[eva] Done for function vwarn +[eva] Recording results for vaw +[eva] Done for function vaw +[eva] computing for function vawx <- main. + Called from err_h.c:43. +[eva] computing for function vwarnx <- vawx <- main. + Called from err_h.c:19. +[eva] using specification for function vwarnx +[eva] err_h.c:19: + function vwarnx: precondition 'fmt_valid_read_or_null' got status valid. +[eva] Done for function vwarnx +[eva] Recording results for vawx +[eva] Done for function vawx +[eva] computing for function err <- main. + Called from err_h.c:45. +[eva] using specification for function err +[eva] err_h.c:45: + function err: precondition 'fmt_valid_read_or_null' got status valid. +[eva] err_h.c:45: Warning: + cannot interpret assigns clause \exit_status + (unsupported logic var \exit_status); effects will be ignored +[eva] Done for function err +[eva] computing for function err <- main. + Called from err_h.c:49. +[eva] err_h.c:49: + function err: precondition 'fmt_valid_read_or_null' got status valid. +[eva] err_h.c:49: Warning: + cannot interpret assigns clause \exit_status + (unsupported logic var \exit_status); effects will be ignored +[eva] Done for function err +[eva] computing for function errx <- main. + Called from err_h.c:53. +[eva] using specification for function errx +[eva] err_h.c:53: + function errx: precondition 'fmt_valid_read_or_null' got status valid. +[eva] err_h.c:53: Warning: + cannot interpret assigns clause \exit_status + (unsupported logic var \exit_status); effects will be ignored +[eva] Done for function errx +[eva] computing for function vae <- main. + Called from err_h.c:57. +[eva] computing for function verr <- vae <- main. + Called from err_h.c:26. +[eva] using specification for function verr +[eva] err_h.c:26: + function verr: precondition 'fmt_valid_read_or_null' got status valid. +[eva] err_h.c:26: Warning: + cannot interpret assigns clause \exit_status + (unsupported logic var \exit_status); effects will be ignored +[eva] Done for function verr +[eva] Recording results for vae +[eva] Done for function vae +[eva] computing for function vaex <- main. + Called from err_h.c:61. +[eva] computing for function verrx <- vaex <- main. + Called from err_h.c:33. +[eva] using specification for function verrx +[eva] err_h.c:33: + function verrx: precondition 'fmt_valid_read_or_null' got status valid. +[eva] err_h.c:33: Warning: + cannot interpret assigns clause \exit_status + (unsupported logic var \exit_status); effects will be ignored +[eva] Done for function verrx +[eva] Recording results for vaex +[eva] Done for function vaex +[eva] Recording results for main +[eva] done for function main +[eva] ====== VALUES COMPUTED ====== +[eva:final-states] Values at end of function vae: + NON TERMINATING FUNCTION +[eva:final-states] Values at end of function vaex: + NON TERMINATING FUNCTION +[eva:final-states] Values at end of function vaw: + ap ∈ {{ &__va_args_14[0] }} +[eva:final-states] Values at end of function vawx: + ap ∈ {{ &__va_args_16[0] }} +[eva:final-states] Values at end of function main: + a ∈ {1} + p ∈ {2} + __retres ∈ {0} diff --git a/tests/libc/oracle/fc_libc.0.res.oracle b/tests/libc/oracle/fc_libc.0.res.oracle index 96ad940d69778adf1aee7a90d46c4d25b63c46c2..895bbd5f4da69c893b6f69ec798ddffdc7a871f7 100644 --- a/tests/libc/oracle/fc_libc.0.res.oracle +++ b/tests/libc/oracle/fc_libc.0.res.oracle @@ -5,10 +5,10 @@ [eva] Initial state computed [eva:initial-state] Values of globals at initialization -[eva] fc_libc.c:172: assertion got status valid. [eva] fc_libc.c:173: assertion got status valid. [eva] fc_libc.c:174: assertion got status valid. [eva] fc_libc.c:175: assertion got status valid. +[eva] fc_libc.c:176: assertion got status valid. [eva] Recording results for main [eva] done for function main [eva] ====== VALUES COMPUTED ====== @@ -32,6 +32,7 @@ #include "ctype.c" #include "ctype.h" #include "dirent.h" +#include "err.h" #include "errno.c" #include "errno.h" #include "fcntl.h" diff --git a/tests/libc/oracle/fc_libc.1.res.oracle b/tests/libc/oracle/fc_libc.1.res.oracle index 566c5af75a06db659d7b28cacb1c2f2a69a9640d..366cde03330ee143db757c4a4205b6cf4394e771 100644 --- a/tests/libc/oracle/fc_libc.1.res.oracle +++ b/tests/libc/oracle/fc_libc.1.res.oracle @@ -2047,7 +2047,7 @@ static void str_append(char **to, size_t *to_len, char const *buf, error_t argz_replace(char **argz, size_t *argz_len, char const *str, char const *with, unsigned int *replace_count) { - error_t err = 0; + error_t er = 0; if (str) if (*str) { char *arg = (char *)0; @@ -2059,7 +2059,7 @@ error_t argz_replace(char **argz, size_t *argz_len, char const *str, size_t str_len = strlen(str); size_t with_len = strlen(with); while (1) { - if (! err) { + if (! er) { arg = argz_next((char const *)src,src_len,(char const *)arg); if (! arg) break; } @@ -2094,23 +2094,23 @@ error_t argz_replace(char **argz, size_t *argz_len, char const *str, } if (to) { if (delayed_copy) { - if (arg > src) err = argz_append(& dst,& dst_len, - (char const *)src, - (unsigned int)(arg - src)); + if (arg > src) er = argz_append(& dst,& dst_len, + (char const *)src, + (unsigned int)(arg - src)); delayed_copy = 0; } - if (! err) err = argz_add(& dst,& dst_len,(char const *)to); + if (! er) er = argz_add(& dst,& dst_len,(char const *)to); free((void *)to); } - else err = 12; + else er = 12; if (replace_count) (*replace_count) ++; } else - if (! delayed_copy) err = argz_add(& dst,& dst_len, - (char const *)arg); + if (! delayed_copy) er = argz_add(& dst,& dst_len, + (char const *)arg); } } - if (! err) { + if (! er) { if (! delayed_copy) { free((void *)src); *argz = dst; @@ -2120,7 +2120,7 @@ error_t argz_replace(char **argz, size_t *argz_len, char const *str, else if (dst_len > (size_t)0) free((void *)dst); } - return err; + return er; } char *argz_next(char const *argz, size_t argz_len, char const *entry) @@ -8402,6 +8402,62 @@ extern DIR *opendir(char const *path); */ extern struct dirent *readdir(DIR *dirp); +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + ensures never_terminates: \false; + + assigns \exit_status \from eval; + */ +void err(int eval, char const *fmt, void * const *__va_params); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + ensures never_terminates: \false; + + assigns \exit_status \from eval; + */ +void errx(int eval, char const *fmt, void * const *__va_params); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + assigns \nothing; + */ +void warn(char const *fmt, void * const *__va_params); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + assigns \nothing; + */ +void warnx(char const *fmt, void * const *__va_params); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + ensures never_terminates: \false; + + assigns \exit_status \from eval; + */ +void verr(int eval, char const *fmt, va_list args); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + ensures never_terminates: \false; + + assigns \exit_status \from eval; + */ +void verrx(int eval, char const *fmt, va_list args); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + assigns \nothing; + */ +void vwarn(char const *fmt, va_list args); + +/*@ requires + fmt_valid_read_or_null: valid_read_string(fmt) ∨ fmt ≡ \null; + assigns \nothing; + */ +void vwarnx(char const *fmt, va_list args); + /*@ requires valid_res: \valid(res); ensures initialization: res: \initialized(\old(res)); ensures res_wrapped: *\old(res) ≡ (int)(\old(a) + \old(b)); diff --git a/tests/libc/oracle/fc_libc.2.res.oracle b/tests/libc/oracle/fc_libc.2.res.oracle index 9e97d67741a1d5aa5687dc28730ad2aa41f27b7b..7f56a29b964dc433e9e2517346c1ae83b3c73741 100644 --- a/tests/libc/oracle/fc_libc.2.res.oracle +++ b/tests/libc/oracle/fc_libc.2.res.oracle @@ -57,6 +57,7 @@ skipping FRAMAC_SHARE/libc/complex.h [kernel] Parsing FRAMAC_SHARE/libc/dirent.h (with preprocessing) [kernel] Parsing FRAMAC_SHARE/libc/dlfcn.h (with preprocessing) [kernel] Parsing FRAMAC_SHARE/libc/endian.h (with preprocessing) +[kernel] Parsing FRAMAC_SHARE/libc/err.h (with preprocessing) [kernel] Parsing FRAMAC_SHARE/libc/errno.h (with preprocessing) [kernel] Parsing FRAMAC_SHARE/libc/fcntl.h (with preprocessing) [kernel] Parsing FRAMAC_SHARE/libc/features.h (with preprocessing)