From 67f7bfc50305f134821b24b104c4e9e2fe315f7d Mon Sep 17 00:00:00 2001
From: Andre Maroneze <andre.maroneze@cea.fr>
Date: Fri, 30 Sep 2022 16:33:10 +0200
Subject: [PATCH] [libc] improve POSIX and non-POSIX compatibility

---
 share/libc/fcntl.h                            | 15 ++++++---
 share/libc/signal.h                           | 11 +++++++
 share/libc/sys/socket.h                       | 33 ++++++++++---------
 share/libc/sys/types.h                        |  5 +++
 .../tests/known/oracle/fcntl.res.oracle       |  6 ++--
 .../tests/known/oracle/open.res.oracle        |  6 ++--
 .../tests/known/oracle/open_wrong.res.oracle  |  6 ++--
 .../tests/known/oracle/openat.res.oracle      |  6 ++--
 tests/libc/oracle/fc_libc.1.res.oracle        |  9 +++++
 tests/libc/oracle/signal_h.res.oracle         | 11 ++++++-
 tests/libc/signal_h.c                         |  9 ++++-
 tests/syntax/oracle/signal.res.oracle         |  1 +
 12 files changed, 84 insertions(+), 34 deletions(-)

diff --git a/share/libc/fcntl.h b/share/libc/fcntl.h
index b5dabf3d70c..5e90adad1c4 100644
--- a/share/libc/fcntl.h
+++ b/share/libc/fcntl.h
@@ -72,6 +72,9 @@ __PUSH_FC_STDLIB
 #define O_RSYNC 0x101000
 #define O_SYNC 0x101000
 
+// Non-POSIX
+#define O_PATH 010000000
+
 #define O_ACCMODE 3
 
 //#define O_EXEC
@@ -81,15 +84,19 @@ __PUSH_FC_STDLIB
 #define O_WRONLY 1
 
 #define AT_FDCWD -100
-
 #define AT_EACCESS 0x200
-
 #define AT_SYMLINK_NOFOLLOW 0x100
-
 #define AT_SYMLINK_FOLLOW 0x400
-
 #define AT_REMOVEDIR 0x200
 
+// Non-POSIX (GNU extensions)
+#define AT_EMPTY_PATH 0x1000
+#define AT_RECURSIVE 0x8000
+#define AT_STATX_DONT_SYNC 0x4000
+#define AT_STATX_FORCE_SYNC 0x2000
+#define AT_STATX_SYNC_AS_STAT 0x0000
+#define AT_STATX_SYNC_TYPE 0x6000
+
 #define POSIX_FADV_DONTNEED 4
 #define POSIX_FADV_NOREUSE 5
 #define POSIX_FADV_NORMAL 0
diff --git a/share/libc/signal.h b/share/libc/signal.h
index b594a4b53e1..09ee3a57aae 100644
--- a/share/libc/signal.h
+++ b/share/libc/signal.h
@@ -31,6 +31,7 @@ __PUSH_FC_STDLIB
 #include "__fc_define_pid_t.h"
 #include "__fc_define_uid_and_gid.h"
 #include "__fc_define_pthread_types.h"
+#include "errno.h"
 
 __BEGIN_DECLS
 
@@ -243,6 +244,16 @@ extern int sigaction(int signum, const struct sigaction *restrict act,
 extern int sigprocmask(int how, const sigset_t * restrict set,
                        sigset_t *restrict oldset);
 
+/*@ // missing: assigns 'current signal mask'
+    // missing: assigns \result from 'possible thread interruption'
+    // missing: terminates 'only when interrupted'
+  requires valid_mask_or_null: sigmask == \null || \valid_read(sigmask);
+  assigns __fc_errno, \result \from indirect:sigmask;
+  ensures result_means_interrupted: \result == -1;
+  ensures errno_set: __fc_errno == EINTR;
+*/
+extern int sigsuspend(const sigset_t *sigmask);
+
 /*@ // missing: errno may be set to EINVAL, EPERM, ESRCH
     // missing: assigns 'other processes' \from 'other processes'
   assigns \result \from indirect:pid, indirect: sig;
diff --git a/share/libc/sys/socket.h b/share/libc/sys/socket.h
index 114226d440b..846a297f0d7 100644
--- a/share/libc/sys/socket.h
+++ b/share/libc/sys/socket.h
@@ -66,6 +66,17 @@ struct msghdr {
   int            msg_flags;
 };
 
+// POSIX requires these as macros; early uses of these macros (e.g. when
+// computing the size of arrays) prevent us from leaving them undefined and
+// using a function prototype. The following definitions are based on the
+// Linux version of the GNU libc.
+#define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
+#define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \
+                     & (size_t) ~(sizeof (size_t) - 1))
+#define CMSG_SPACE(len) (CMSG_ALIGN (len) \
+                       + CMSG_ALIGN (sizeof (struct cmsghdr)))
+#define CMSG_LEN(len)   (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
+
 // POSIX.1-2008 requires these to be defined as macros, but we have no body
 // for them, so we declare them as prototypes as well.
 #ifndef CMSG_FIRSTHDR
@@ -76,22 +87,6 @@ extern struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
 extern struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
 # define CMSG_NXTHDR(h, c) CMSG_NXTHDR(h, c)
 #endif
-#ifndef CMSG_ALIGN
-extern size_t CMSG_ALIGN(size_t length);
-# define CMSG_ALIGN(l) CMSG_ALIGN(l)
-#endif
-#ifndef CMSG_SPACE
-extern size_t CMSG_SPACE(size_t length);
-# define CMSG_SPACE(l) CMSG_SPACE(l)
-#endif
-#ifndef CMSG_LEN
-extern size_t CMSG_LEN(size_t length);
-# define CMSG_LEN(l) CMSG_LEN(l)
-#endif
-#ifndef CMSG_DATA
-extern unsigned char *CMSG_DATA(struct cmsghdr *cmsg);
-# define CMSG_DATA(c) CMSG_DATA(c)
-#endif
 
 /* Socket types. */
 #define SOCK_STREAM	1		/* stream (connection) socket	*/
@@ -295,6 +290,12 @@ struct __fc_sockfds_type { int x; };
 
 // __fc_sockfds represents the state of open socket descriptors.
 //@ ghost volatile int __fc_open_sock_fds;
+
+struct linger {
+  int l_onoff;
+  int l_linger;
+};
+
 // TODO: Model the state of some functions more precisely.
 
 /*@
diff --git a/share/libc/sys/types.h b/share/libc/sys/types.h
index 68f3deb924a..be288c41c42 100644
--- a/share/libc/sys/types.h
+++ b/share/libc/sys/types.h
@@ -59,6 +59,11 @@ typedef unsigned char u_char;
 #define __u_char_defined
 #endif
 
+// Non-POSIX
+#ifndef caddr_t
+typedef char *caddr_t;
+#endif
+
 __END_DECLS
 __POP_FC_STDLIB
 #endif
diff --git a/src/plugins/variadic/tests/known/oracle/fcntl.res.oracle b/src/plugins/variadic/tests/known/oracle/fcntl.res.oracle
index ff5a912894d..bb16be630f9 100644
--- a/src/plugins/variadic/tests/known/oracle/fcntl.res.oracle
+++ b/src/plugins/variadic/tests/known/oracle/fcntl.res.oracle
@@ -1,7 +1,7 @@
-[variadic] FRAMAC_SHARE/libc/fcntl.h:121: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:128: 
   Declaration of variadic function fcntl.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:124: Declaration of variadic function open.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:131: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:131: Declaration of variadic function open.
+[variadic] FRAMAC_SHARE/libc/fcntl.h:138: 
   Declaration of variadic function openat.
 [variadic] fcntl.c:8: 
   Translating call to the specialized version fcntl(int, int).
diff --git a/src/plugins/variadic/tests/known/oracle/open.res.oracle b/src/plugins/variadic/tests/known/oracle/open.res.oracle
index 9b5c71ca58f..190fa3b120e 100644
--- a/src/plugins/variadic/tests/known/oracle/open.res.oracle
+++ b/src/plugins/variadic/tests/known/oracle/open.res.oracle
@@ -1,7 +1,7 @@
-[variadic] FRAMAC_SHARE/libc/fcntl.h:121: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:128: 
   Declaration of variadic function fcntl.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:124: Declaration of variadic function open.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:131: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:131: Declaration of variadic function open.
+[variadic] FRAMAC_SHARE/libc/fcntl.h:138: 
   Declaration of variadic function openat.
 [variadic] open.c:7: 
   Translating call to the specialized version open(char const *, int, mode_t).
diff --git a/src/plugins/variadic/tests/known/oracle/open_wrong.res.oracle b/src/plugins/variadic/tests/known/oracle/open_wrong.res.oracle
index 1f4b8a7cd11..a3eb22c2716 100644
--- a/src/plugins/variadic/tests/known/oracle/open_wrong.res.oracle
+++ b/src/plugins/variadic/tests/known/oracle/open_wrong.res.oracle
@@ -1,7 +1,7 @@
-[variadic] FRAMAC_SHARE/libc/fcntl.h:121: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:128: 
   Declaration of variadic function fcntl.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:124: Declaration of variadic function open.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:131: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:131: Declaration of variadic function open.
+[variadic] FRAMAC_SHARE/libc/fcntl.h:138: 
   Declaration of variadic function openat.
 [variadic:prototype] open_wrong.c:13: Warning: 
   No matching prototype found for this call to open.
diff --git a/src/plugins/variadic/tests/known/oracle/openat.res.oracle b/src/plugins/variadic/tests/known/oracle/openat.res.oracle
index 30a3448710c..96183ba1107 100644
--- a/src/plugins/variadic/tests/known/oracle/openat.res.oracle
+++ b/src/plugins/variadic/tests/known/oracle/openat.res.oracle
@@ -1,7 +1,7 @@
-[variadic] FRAMAC_SHARE/libc/fcntl.h:121: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:128: 
   Declaration of variadic function fcntl.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:124: Declaration of variadic function open.
-[variadic] FRAMAC_SHARE/libc/fcntl.h:131: 
+[variadic] FRAMAC_SHARE/libc/fcntl.h:131: Declaration of variadic function open.
+[variadic] FRAMAC_SHARE/libc/fcntl.h:138: 
   Declaration of variadic function openat.
 [variadic] openat.c:8: 
   Translating call to the specialized version openat(int, char const *, int, mode_t).
diff --git a/tests/libc/oracle/fc_libc.1.res.oracle b/tests/libc/oracle/fc_libc.1.res.oracle
index a081af02056..4e29fd1c1c8 100644
--- a/tests/libc/oracle/fc_libc.1.res.oracle
+++ b/tests/libc/oracle/fc_libc.1.res.oracle
@@ -4465,6 +4465,15 @@ extern int sigaction(int signum, struct sigaction const * restrict act,
 extern int sigprocmask(int how, sigset_t const * restrict set,
                        sigset_t * restrict oldset);
 
+/*@ requires valid_mask_or_null: sigmask ≡ \null ∨ \valid_read(sigmask);
+    ensures result_means_interrupted: \result ≡ -1;
+    ensures errno_set: __fc_errno ≡ 4;
+    assigns __fc_errno, \result;
+    assigns __fc_errno \from (indirect: sigmask);
+    assigns \result \from (indirect: sigmask);
+ */
+extern int sigsuspend(sigset_t const *sigmask);
+
 /*@ ensures result_ok_or_error: \result ≡ 0 ∨ \result ≡ -1;
     assigns \result;
     assigns \result \from (indirect: pid), (indirect: sig);
diff --git a/tests/libc/oracle/signal_h.res.oracle b/tests/libc/oracle/signal_h.res.oracle
index 0141d180b8f..7c70744fb71 100644
--- a/tests/libc/oracle/signal_h.res.oracle
+++ b/tests/libc/oracle/signal_h.res.oracle
@@ -126,7 +126,7 @@
   function sigaction: precondition 'valid_read_act_or_null' got status valid.
 [eva] signal_h.c:48: 
   function sigaction: precondition 'separation,separated_acts' got status valid.
-[eva] FRAMAC_SHARE/libc/signal.h:222: 
+[eva] FRAMAC_SHARE/libc/signal.h:223: 
   cannot evaluate ACSL term, unsupported ACSL construct: logic coercion struct sigaction -> set<struct sigaction>
 [eva] Done for function sigaction
 [eva] computing for function sigaction <- main.
@@ -143,10 +143,19 @@
   Completely invalid destination for assigns clause *oldact. Ignoring.
 [eva] Done for function sigaction
 [eva] signal_h.c:55: assertion 'valid_nsig' got status valid.
+[eva] computing for function sigsuspend <- main.
+  Called from signal_h.c:59.
+[eva] using specification for function sigsuspend
+[eva] signal_h.c:59: 
+  function sigsuspend: precondition 'valid_mask_or_null' got status valid.
+[eva] Done for function sigsuspend
+[eva] signal_h.c:60: assertion 'sigsuspend_errno_eintr' got status valid.
+[eva] signal_h.c:61: assertion 'sigsuspend_return' got status valid.
 [eva] Recording results for main
 [eva] done for function main
 [eva] ====== VALUES COMPUTED ======
 [eva:final-states] Values at end of function main:
+  __fc_errno ∈ [--..--]
   __fc_sigaction[0]{.sa_handler; .sa_sigaction} ∈ {0}
                 [0]{.sa_mask; .sa_flags} ∈ [--..--]
                 [1]{.sa_handler; .sa_sigaction} ∈ {0}
diff --git a/tests/libc/signal_h.c b/tests/libc/signal_h.c
index bfb2fb979f9..0ece2aacd90 100644
--- a/tests/libc/signal_h.c
+++ b/tests/libc/signal_h.c
@@ -1,8 +1,8 @@
 /* run.config
    STDOPT: #"-eva-slevel 2"
 */
+#include <errno.h>
 #include <signal.h>
-
 volatile int nondet;
 
 int main() {
@@ -54,5 +54,12 @@ int main() {
 
   //@ assert valid_nsig: NSIG >= 0;
 
+  if (nondet) {
+    errno = 0;
+    int r = sigsuspend(&s);
+    //@ assert sigsuspend_errno_eintr: errno == EINTR;
+    //@ assert sigsuspend_return: r == -1;
+  }
+
   return 0;
 }
diff --git a/tests/syntax/oracle/signal.res.oracle b/tests/syntax/oracle/signal.res.oracle
index 8ce74a3d613..cda400014c7 100644
--- a/tests/syntax/oracle/signal.res.oracle
+++ b/tests/syntax/oracle/signal.res.oracle
@@ -1,5 +1,6 @@
 [kernel] Parsing signal.c (with preprocessing)
 /* Generated by Frama-C */
+#include "errno.h"
 #include "signal.h"
 void f(void)
 {
-- 
GitLab