From 366129663b9c164822a733fd73c2361662ec7247 Mon Sep 17 00:00:00 2001
From: Basile Desloges <basile.desloges@cea.fr>
Date: Tue, 28 Jan 2025 15:55:41 +0100
Subject: [PATCH] [kernel] Fix typing of types in builtins

The builtins `__builtin_types_compatible_p` and `__builtin_va_arg` need
a type as argument. Frama-C converts them to an expression to be able to
type the builtin call.

This commit change the conversion in `cparser.mly` from a sizeof on the
type to a cast with a marker indicating from which builtin the argument
is coming from. This allows `cabs2cil` to type this expression
differently than a regular cast or sizeof.
---
 src/kernel_internals/parsing/cparser.mly      | 24 ++++++++++++-----
 src/kernel_internals/typing/cabs2cil.ml       | 26 +++++++++++++++++++
 .../ast_printing/cil_printer.ml               |  2 +-
 3 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/src/kernel_internals/parsing/cparser.mly b/src/kernel_internals/parsing/cparser.mly
index 653febf25e..16b088b68f 100644
--- a/src/kernel_internals/parsing/cparser.mly
+++ b/src/kernel_internals/parsing/cparser.mly
@@ -313,6 +313,15 @@ let in_ghost_block ?(battrs=[]) l =
                      get_statementloc (List.hd l),
                      get_statementloc (Extlib.last l)))
 
+let type_to_expr_for_builtin ~loc ~builtin specifier decl_type =
+  (* Prefix the builtin name with __fc_ to avoid collisions with real
+     world code. *)
+  let builtin = "__fc_" ^ builtin in
+  (* Build an expression representing the given type with a marker to indicate
+     the builtin for which this translation was made. *)
+  let info = SINGLE_INIT { expr_loc = loc; expr_node = VARIABLE builtin } in
+  { expr_loc = loc; expr_node = CAST ((specifier, decl_type), info) }
+
 %}
 
 %token <Filepath.position * string> SPEC
@@ -551,28 +560,31 @@ postfix_expression:                     /*(* 6.5.2 *)*/
 | postfix_expression LPAREN arguments RPAREN ghost_arguments_opt
     { make_expr $sloc (CALL ($1, $3, $5))}
 | BUILTIN_VA_ARG LPAREN expression COMMA type_name RPAREN {
+    let builtin = "__builtin_va_arg" in
     let b, d = $5 in
     let loc = Cil_datatype.Location.of_lexing_loc $loc($5) in
     let loc_f = Cil_datatype.Location.of_lexing_loc $loc($1) in
+    let type_as_expr = type_to_expr_for_builtin ~loc ~builtin b d in
     make_expr $sloc
       (CALL
           ({ expr_loc = loc_f;
-            expr_node = VARIABLE "__builtin_va_arg"},
-          [$3; { expr_loc = loc;
-                  expr_node = TYPE_SIZEOF (b, d)}],[]))
+            expr_node = VARIABLE builtin},
+          [$3; type_as_expr],[]))
   }
 | BUILTIN_TYPES_COMPAT LPAREN type_name COMMA type_name RPAREN {
+    let builtin = "__builtin_types_compatible_p" in
     let b1,d1 = $3 in
     let b2,d2 = $5 in
     let loc_f = Cil_datatype.Location.of_lexing_loc $loc($1) in
     let loc1 = Cil_datatype.Location.of_lexing_loc $loc($3) in
     let loc2 = Cil_datatype.Location.of_lexing_loc $loc($5) in
+    let type_as_expr1 = type_to_expr_for_builtin ~loc:loc1 ~builtin b1 d1 in
+    let type_as_expr2 = type_to_expr_for_builtin ~loc:loc2 ~builtin b2 d2 in
     make_expr $sloc
       (CALL
           ({expr_loc = loc_f;
-            expr_node = VARIABLE "__builtin_types_compatible_p"},
-          [ { expr_loc = loc1; expr_node = TYPE_SIZEOF(b1,d1)};
-            { expr_loc = loc2; expr_node = TYPE_SIZEOF(b2,d2)}],[]))
+            expr_node = VARIABLE builtin},
+          [ type_as_expr1; type_as_expr2],[]))
   }
 | BUILTIN_OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN {
     let loc_f = Cil_datatype.Location.of_lexing_loc $loc($1) in
diff --git a/src/kernel_internals/typing/cabs2cil.ml b/src/kernel_internals/typing/cabs2cil.ml
index 0d523e85c3..8ca38fc507 100644
--- a/src/kernel_internals/typing/cabs2cil.ml
+++ b/src/kernel_internals/typing/cabs2cil.ml
@@ -265,6 +265,12 @@ let rec stripParen e =
   | { expr_node = Cabs.PAREN e } -> stripParen e
   | e -> e
 
+let is_for_builtin builtin info =
+  match info with
+  | SINGLE_INIT { expr_node = VARIABLE name } ->
+    String.equal ("__fc_" ^ builtin) name
+  | _ -> false
+
 let rec is_dangerous_offset = function
     NoOffset -> false
   | Field (fi, o) ->
@@ -5818,6 +5824,26 @@ and doExp local_env
       finishExp [] scope_chunk (new_exp ~loc (AlignOfE(e'')))
         (Machine.sizeof_type ())
 
+    (* In cparser, the types used as arguments of certain builtins are converted
+       to casts so that they can be represented as expressions. The following
+       matches are special cases to type those expressions. They are then
+       converted to `sizeof typ` for CIL. *)
+    | Cabs.CAST ((specs, dt), info)
+      when is_for_builtin "__builtin_types_compatible_p" info ->
+      let typ = doOnlyType loc local_env.is_ghost specs dt in
+      let res = new_exp ~loc (SizeOf typ) in
+      finishExp [] (unspecified_chunk empty) res (Machine.sizeof_type ())
+
+    | Cabs.CAST ((specs, dt), info)
+      when is_for_builtin "__builtin_va_arg" info ->
+      let typ = doOnlyType loc local_env.is_ghost specs dt in
+      if not (Cil.isCompleteType typ) then
+        Kernel.error ~current:true "__builtin_va_arg on incomplete type '%a'"
+          Cil_datatype.Typ.pretty typ;
+      let res = new_exp ~loc (SizeOf typ) in
+      finishExp [] (unspecified_chunk empty) res (Machine.sizeof_type ())
+    (* End of special casts. *)
+
     | Cabs.CAST ((specs, dt), ie) ->
       let s', dt', ie' = preprocessCast loc local_env.is_ghost specs dt ie in
       (* We know now that we can do s' and dt' many times *)
diff --git a/src/kernel_services/ast_printing/cil_printer.ml b/src/kernel_services/ast_printing/cil_printer.ml
index f3bc4b667c..43115b0c53 100644
--- a/src/kernel_services/ast_printing/cil_printer.ml
+++ b/src/kernel_services/ast_printing/cil_printer.ml
@@ -1097,7 +1097,7 @@ class cil_printer () = object (self)
       let last = self#getLastNamedArgument () in
       self#instr fmt (Call(res,Cil.dummy_exp(Lval(Var vi,NoOffset)),[last],l))
 
-    (* In cparser we have turned the call to
+    (* In cabs2cil we have turned the call to
        __builtin_types_compatible_p(t1, t2) into
        __builtin_types_compatible_p(sizeof t1, sizeof t2), so that we can
        represent the types as expressions.
-- 
GitLab