|
| 1 | +(* ============================================================================= |
| 2 | + CodeHawk C Analyzer |
| 3 | + Author: Henny Sipma |
| 4 | + ------------------------------------------------------------------------------ |
| 5 | + The MIT License (MIT) |
| 6 | +
|
| 7 | + Copyright (c) 2025 Aarno Labs LLC |
| 8 | +
|
| 9 | + Permission is hereby granted, free of charge, to any person obtaining a copy |
| 10 | + of this software and associated documentation files (the "Software"), to deal |
| 11 | + in the Software without restriction, including without limitation the rights |
| 12 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 13 | + copies of the Software, and to permit persons to whom the Software is |
| 14 | + furnished to do so, subject to the following conditions: |
| 15 | +
|
| 16 | + The above copyright notice and this permission notice shall be included in all |
| 17 | + copies or substantial portions of the Software. |
| 18 | +
|
| 19 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 20 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 21 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 22 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 23 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 24 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 25 | + SOFTWARE. |
| 26 | + ============================================================================= *) |
| 27 | + |
| 28 | +(* chlib *) |
| 29 | +open CHPretty |
| 30 | + |
| 31 | +(* chutil *) |
| 32 | +open CHLogger |
| 33 | +open CHTimingLog |
| 34 | + |
| 35 | +(* cchlib *) |
| 36 | +open CCHBasicTypes |
| 37 | +open CCHBasicUtil |
| 38 | +open CCHContext |
| 39 | +open CCHDeclarations |
| 40 | +open CCHFileContract |
| 41 | +open CCHLibTypes |
| 42 | +open CCHSettings |
| 43 | +open CCHTypesUtil |
| 44 | +open CCHUtilities |
| 45 | + |
| 46 | +(* cchpre *) |
| 47 | +open CCHPreFileIO |
| 48 | +open CCHPreTypes |
| 49 | +open CCHProofScaffolding |
| 50 | + |
| 51 | + |
| 52 | +let fenv = CCHFileEnvironment.file_environment |
| 53 | + |
| 54 | + |
| 55 | +class po_creator_t (f:fundec) (pointer_parameters: varinfo list) = |
| 56 | +object (self) |
| 57 | + |
| 58 | + method private f = f |
| 59 | + |
| 60 | + method private ftype = self#f.svar.vtype |
| 61 | + |
| 62 | + method private fname = self#f.svar.vname |
| 63 | + |
| 64 | + method private pointer_parameters = pointer_parameters |
| 65 | + |
| 66 | + method private env = self#f.sdecls |
| 67 | + |
| 68 | + method private spomanager = proof_scaffolding#get_spo_manager self#fname |
| 69 | + |
| 70 | + method private add_ppo |
| 71 | + (pred: po_predicate_t) |
| 72 | + (loc: location) |
| 73 | + (ctxt: program_context_int) = |
| 74 | + (proof_scaffolding#get_ppo_manager self#fname)#add_ppo loc ctxt pred |
| 75 | + |
| 76 | + method create_proof_obligations = |
| 77 | + self#create_po_block (mk_program_context ()) self#f.sbody |
| 78 | + |
| 79 | + method private create_po_block (context: program_context_int) (b: block) = |
| 80 | + List.iter (fun s -> self#create_po_stmt (context#add_stmt s.sid) s) b.bstmts |
| 81 | + |
| 82 | + method private create_po_stmt (context: program_context_int) (s: stmt) = |
| 83 | + self#create_po_stmtkind context s.skind |
| 84 | + |
| 85 | + method private create_po_stmtkind (context: program_context_int) (k: stmtkind) = |
| 86 | + match k with |
| 87 | + | Instr l -> |
| 88 | + List.iteri |
| 89 | + (fun i instr -> self#create_po_instr (context#add_instr i) instr) l |
| 90 | + |
| 91 | + | Return (e, loc) -> self#create_po_return context e loc |
| 92 | + |
| 93 | + | ComputedGoto (e, loc) -> self#create_po_computed_goto context e loc |
| 94 | + |
| 95 | + | If (e, thenblock, elseblock, loc) -> |
| 96 | + self#create_po_if context e thenblock elseblock loc |
| 97 | + |
| 98 | + | Switch (e, b, _, loc) -> self#create_po_switch context e b loc |
| 99 | + |
| 100 | + | Loop (b, loc, _, _) -> self#create_po_loop context#add_loop b loc |
| 101 | + |
| 102 | + | Block b -> self#create_po_block context b |
| 103 | + |
| 104 | + | Goto _ | Break _ | Continue _ -> () |
| 105 | + |
| 106 | + | TryFinally _ | TryExcept _ -> |
| 107 | + pr_debug [ |
| 108 | + STR "proof obligations for TryFinally/TryExcept not supported"; NL] |
| 109 | + |
| 110 | + method private create_po_loop |
| 111 | + (context: program_context_int) (b: block) (_loc: location) = |
| 112 | + self#create_po_block context b |
| 113 | + |
| 114 | + method private create_po_switch |
| 115 | + (context: program_context_int) |
| 116 | + (e: exp) |
| 117 | + (b: block) |
| 118 | + (loc: location) = |
| 119 | + begin |
| 120 | + self#create_po_exp context#add_switch_expr e loc; |
| 121 | + self#create_po_block context b |
| 122 | + end |
| 123 | + |
| 124 | + method private create_po_if |
| 125 | + (context: program_context_int) |
| 126 | + (e: exp) |
| 127 | + (tb: block) |
| 128 | + (eb: block) |
| 129 | + (loc: location) = |
| 130 | + begin |
| 131 | + self#create_po_exp context#add_if_expr e loc; |
| 132 | + self#create_po_block context#add_if_then tb; |
| 133 | + self#create_po_block context#add_if_else eb |
| 134 | + end |
| 135 | + |
| 136 | + method private create_po_computed_goto |
| 137 | + (context: program_context_int) (e: exp) (loc: location) = |
| 138 | + self#create_po_exp context#add_goto e loc |
| 139 | + |
| 140 | + method private create_po_return |
| 141 | + (context: program_context_int) (e:exp option) (loc: location) = |
| 142 | + let _ = self#spomanager#add_return loc context#add_return e in |
| 143 | + begin |
| 144 | + (match e with |
| 145 | + | Some x -> self#create_po_exp context#add_return x loc |
| 146 | + | _ -> ()); |
| 147 | + (List.iter (fun vinfo -> |
| 148 | + begin |
| 149 | + self#add_ppo |
| 150 | + (POutputParameterInitialized vinfo) loc context#add_return; |
| 151 | + self#add_ppo |
| 152 | + (POutputParameterUnaltered vinfo) loc context#add_return |
| 153 | + end) |
| 154 | + self#pointer_parameters) |
| 155 | + end |
| 156 | + |
| 157 | + method private create_po_instr (context: program_context_int) (i: instr) = |
| 158 | + match i with |
| 159 | + | Set (lval, e, loc) -> self#create_po_assignment context lval e loc |
| 160 | + | Call (lval_o, e, el, loc) -> self#create_po_call context lval_o e el loc |
| 161 | + | Asm _ -> () |
| 162 | + | VarDecl _ -> () |
| 163 | + |
| 164 | + method private create_po_assignment |
| 165 | + (context: program_context_int) |
| 166 | + (lval: lval) |
| 167 | + (e: exp) |
| 168 | + (loc: location) = |
| 169 | + begin |
| 170 | + self#create_po_lval context#add_lhs lval loc; |
| 171 | + self#create_po_exp context#add_rhs e loc |
| 172 | + end |
| 173 | + |
| 174 | + method private create_po_call |
| 175 | + (context: program_context_int) |
| 176 | + (lval_o: lval option) |
| 177 | + (e: exp) |
| 178 | + (el: exp list) |
| 179 | + (loc: location) = |
| 180 | + begin |
| 181 | + (match lval_o with |
| 182 | + | Some lval -> self#create_po_lval context#add_lhs lval loc |
| 183 | + | _ -> ()); |
| 184 | + (match e with |
| 185 | + | Lval (Var (_vname, vid), NoOffset) -> |
| 186 | + self#spomanager#add_direct_call |
| 187 | + loc context (self#env#get_varinfo_by_vid vid) el; |
| 188 | + | _ -> |
| 189 | + begin |
| 190 | + self#create_po_exp context#add_ftarget e loc; |
| 191 | + self#spomanager#add_indirect_call loc context e el |
| 192 | + end); |
| 193 | + (List.iteri (fun i x -> |
| 194 | + let newcontext = context#add_arg (i + 1) in |
| 195 | + self#create_po_exp newcontext x loc) el) |
| 196 | + end |
| 197 | + |
| 198 | + method private create_po_exp |
| 199 | + (context: program_context_int) |
| 200 | + (x: exp) |
| 201 | + (loc: location) = |
| 202 | + let has_struct_type vid = |
| 203 | + let vinfo = self#env#get_varinfo_by_vid vid in |
| 204 | + match fenv#get_type_unrolled vinfo.vtype with |
| 205 | + | TComp _ -> true |
| 206 | + | _ -> false in |
| 207 | + match x with |
| 208 | + | Const _ -> () |
| 209 | + |
| 210 | + | Lval (Var (vname, vid), NoOffset) when has_struct_type vid -> |
| 211 | + let vinfo = self#env#get_varinfo_by_vid vid in |
| 212 | + begin |
| 213 | + match fenv#get_type_unrolled vinfo.vtype with |
| 214 | + | TComp (tckey, _) -> |
| 215 | + let cinfo = fenv#get_comp tckey in |
| 216 | + begin |
| 217 | + List.iter (fun f -> |
| 218 | + self#add_ppo |
| 219 | + (PInitialized |
| 220 | + (Var (vname, vid), Field ((f.fname, f.fckey), NoOffset))) |
| 221 | + loc |
| 222 | + context) |
| 223 | + cinfo.cfields; |
| 224 | + self#create_po_lval context#add_lval (Var (vname, vid), NoOffset) loc |
| 225 | + end |
| 226 | + | _ -> () |
| 227 | + end |
| 228 | + |
| 229 | + | Lval ((Var _, NoOffset) as lval) -> |
| 230 | + self#add_ppo (PInitialized lval) loc context |
| 231 | + |
| 232 | + | Lval lval -> |
| 233 | + begin |
| 234 | + (List.iter (fun vinfo -> |
| 235 | + self#add_ppo (PLocallyInitialized (vinfo, lval)) loc context) |
| 236 | + self#pointer_parameters); |
| 237 | + self#create_po_lval context#add_lval lval loc |
| 238 | + end |
| 239 | + |
| 240 | + | SizeOf _ | SizeOfE _ | SizeOfStr _ -> () |
| 241 | + |
| 242 | + | AlignOf _ | AlignOfE _ -> () |
| 243 | + |
| 244 | + | UnOp (_, e, _t) -> self#create_po_exp context#add_unop e loc |
| 245 | + |
| 246 | + | BinOp (binop, e1, e2, t) -> |
| 247 | + begin |
| 248 | + self#create_po_exp (context#add_binop 1) e1 loc; |
| 249 | + self#create_po_exp (context#add_binop 2) e2 loc; |
| 250 | + self#create_po_binop context binop e1 e2 t loc |
| 251 | + end |
| 252 | + |
| 253 | + | Question (e1, e2, e3, _t) -> |
| 254 | + begin |
| 255 | + self#create_po_exp (context#add_question 1) e1 loc; |
| 256 | + self#create_po_exp (context#add_question 2) e2 loc; |
| 257 | + self#create_po_exp (context#add_question 3) e3 loc |
| 258 | + end |
| 259 | + |
| 260 | + | CastE (_t, e) -> self#create_po_exp context#add_cast e loc |
| 261 | + |
| 262 | + | AddrOf l -> self#create_po_lval context#add_addrof l loc |
| 263 | + |
| 264 | + | AddrOfLabel _ -> () |
| 265 | + |
| 266 | + | StartOf l -> self#create_po_lval context#add_startof l loc |
| 267 | + |
| 268 | + | FnApp _ | CnApp _ -> () |
| 269 | + |
| 270 | + method private create_po_lval |
| 271 | + (context: program_context_int) |
| 272 | + ((host, offset): lval) |
| 273 | + (loc: location) = |
| 274 | + match host with |
| 275 | + | Var (_vname, vid) -> |
| 276 | + let ty = (self#env#get_varinfo_by_vid vid).vtype in |
| 277 | + self#create_po_offset context#add_var offset ty loc |
| 278 | + | Mem e -> self#create_po_exp context#add_mem e loc |
| 279 | + |
| 280 | + method private create_po_offset |
| 281 | + (context: program_context_int) |
| 282 | + (o: offset) |
| 283 | + (hosttyp: typ) |
| 284 | + (loc: location) = |
| 285 | + match o with |
| 286 | + | NoOffset -> () |
| 287 | + |
| 288 | + | Field ((fname, ckey), oo) -> |
| 289 | + (match fenv#get_type_unrolled hosttyp with |
| 290 | + | TComp (tckey, _) -> |
| 291 | + if tckey = ckey then |
| 292 | + let ftype = fenv#get_field_type ckey fname in |
| 293 | + self#create_po_offset (context#add_field_offset fname) oo ftype loc |
| 294 | + else |
| 295 | + () |
| 296 | + | _ -> ()) |
| 297 | + |
| 298 | + | Index (exp, oo) -> |
| 299 | + (match fenv#get_type_unrolled hosttyp with |
| 300 | + | TArray (tt, Some _len, _) -> |
| 301 | + begin |
| 302 | + self#create_po_exp context#add_index exp loc; |
| 303 | + self#create_po_offset context#add_index_offset oo tt loc |
| 304 | + end |
| 305 | + | _ -> ()) |
| 306 | + |
| 307 | + method private create_po_binop |
| 308 | + (_context: program_context_int) |
| 309 | + (_binop: binop) |
| 310 | + (_e1: exp) |
| 311 | + (_e2: exp) |
| 312 | + (_t: typ) |
| 313 | + (_loc:location) = |
| 314 | + () |
| 315 | + |
| 316 | +end |
| 317 | + |
| 318 | + |
| 319 | +let process_function (fname:string) = |
| 320 | + let _ = log_info "Process function %s [%s:%d]" fname __FILE__ __LINE__ in |
| 321 | + try |
| 322 | + let fundec = read_function_semantics fname in |
| 323 | + let fdecls = fundec.sdecls in |
| 324 | + let ftype = fundec.svar.vtype in |
| 325 | + let pointer_parameters = |
| 326 | + match ftype with |
| 327 | + | TFun (_, Some funargs, _, _) | TPtr (TFun (_, Some funargs, _, _), _) -> |
| 328 | + List.filter (fun (_, ty, _) -> |
| 329 | + (not (has_const_attribute ty)) |
| 330 | + && (match ty with |
| 331 | + | TPtr (tgt, _) -> not (has_const_attribute tgt) |
| 332 | + | _ -> false)) funargs |
| 333 | + | _ -> [] in |
| 334 | + let pointer_parameters = |
| 335 | + List.map (fun (vname, _, _) -> |
| 336 | + fdecls#get_varinfo_by_name vname) pointer_parameters in |
| 337 | + if (List.length pointer_parameters) > 0 then |
| 338 | + begin |
| 339 | + read_proof_files fname fdecls; |
| 340 | + (new po_creator_t fundec pointer_parameters)#create_proof_obligations; |
| 341 | + CCHCheckValid.process_function fname; |
| 342 | + save_proof_files fname; |
| 343 | + save_api fname; |
| 344 | + end |
| 345 | + with |
| 346 | + | CCHFailure p -> |
| 347 | + begin |
| 348 | + pr_debug [ |
| 349 | + STR "Error in processing function "; STR fname; STR ": "; p; NL]; |
| 350 | + ch_error_log#add |
| 351 | + "failure" (LBLOCK [STR "function "; STR fname; STR ": "; p]) |
| 352 | + end |
| 353 | + | Invalid_argument s -> |
| 354 | + ch_error_log#add |
| 355 | + "failure" (LBLOCK [ STR "function "; STR fname; STR ": "; STR s]) |
| 356 | + |
| 357 | + |
| 358 | +let output_parameter_po_process_file () = |
| 359 | + try |
| 360 | + let cfilename = system_settings#get_cfilename in |
| 361 | + let _ = read_cfile_dictionary () in |
| 362 | + let _ = read_cfile_interface_dictionary () in |
| 363 | + let cfile = read_cfile () in |
| 364 | + let _ = fenv#initialize cfile in |
| 365 | + let _ = cdeclarations#index_location call_sink in |
| 366 | + let functions = fenv#get_application_functions in |
| 367 | + let functions = List.filter (fun f -> not (f.vname = "main")) functions in |
| 368 | + let _ = |
| 369 | + log_info |
| 370 | + "Cfile %s initialized with %d functions [%s:%d]" |
| 371 | + cfilename (List.length functions) |
| 372 | + __FILE__ __LINE__ in |
| 373 | + let _ = read_cfile_contract () in |
| 374 | + let _ = file_contract#collect_file_attributes in |
| 375 | + begin |
| 376 | + List.iter (fun f -> process_function f.vname) functions; |
| 377 | + (* List.iter process_global cfile.globals; *) |
| 378 | + save_cfile_assignment_dictionary (); |
| 379 | + save_cfile_predicate_dictionary (); |
| 380 | + save_cfile_interface_dictionary(); |
| 381 | + save_cfile_dictionary (); |
| 382 | + save_cfile_context (); |
| 383 | + end |
| 384 | + with |
| 385 | + | CHXmlReader.IllFormed -> |
| 386 | + ch_error_log#add "ill-formed content" (STR system_settings#get_cfilename) |
0 commit comments