aboutsummaryrefslogtreecommitdiffstats
path: root/checklink/Fuzz.ml
blob: 2fe29b0f01a179d743cd7c477ca18dfbe9994163 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
open Check
open ELF_parsers
open ELF_types
open Frameworks
open Library

let string_of_byte = Printf.sprintf "0x%02x"

let full_range_of_byte elfmap byte =
  let byte = int_int32 byte in
  List.find (fun (a, b, _, _) -> a <= byte && byte <= b) elfmap 

let range_of_byte elfmap byte =
  let (_, _, _, r) = full_range_of_byte elfmap byte in
  r

let fuzz_check elfmap bs byte old sdumps =
  let (str, _, _) = bs in
  try (
    let elf = read_elf_bs bs in
    try (
      let efw = check_elf elf sdumps in
      try (
        let _ = List.find (function ERROR(s) -> true | _ -> false) efw.log in
        ()
      ) with
      | Not_found ->
          print_endline (
            string_of_int32 (int_int32 byte) ^ " <- " ^
              string_of_byte (Char.code str.[byte]) ^ " (was " ^
              string_of_byte (Char.code old) ^ ") - " ^
              string_of_byte_chunk_desc (range_of_byte elfmap byte)
          )
    ) with
    | e -> ()
  ) with
  | e -> ()

let ok_fuzz elfmap str byte fuzz =
  let (a, b, _, r) = full_range_of_byte elfmap byte in
  let a = int32_int a in
  let b = int32_int b in
  let old = Char.code str.[byte] in
  let fuz = Char.code fuzz in
  match r with
  | ELF_header         ->
      not (List.mem byte
             [
               0x18; 0x19; 0x1a; 0x1b; (* e_entry *)
               0x1c; 0x1d; 0x1e; 0x1f; (* e_phoff *)
               0x24; 0x25; 0x26; 0x27; (* e_flags *)
               0x2c; 0x2d (* e_phnum *)
             ]
      )
  | ELF_progtab        -> false
  | ELF_shtab          -> false
  | ELF_section_strtab -> false
  | ELF_symbol_strtab  -> false
  | Symtab_data(_)     ->
      (* False positive: switching from/to STT_NOTYPE *)
      not (byte = a + 12
          && ((old land 0xf = 0) || (fuz land 0xf = 0))
      )
  | Symtab_function(_) -> true
  | Data_symbol(_)     ->
      (* False positive: 0. becomes -0. *)
      not (
        (byte + 7 <= b)
        && (fuz = 0x80)
        && (Char.code str.[byte + 0] = 0x00)
        && (Char.code str.[byte + 1] = 0x00)
        && (Char.code str.[byte + 2] = 0x00)
        && (Char.code str.[byte + 3] = 0x00)
        && (Char.code str.[byte + 4] = 0x00)
        && (Char.code str.[byte + 5] = 0x00)
        && (Char.code str.[byte + 6] = 0x00)
        && (Char.code str.[byte + 7] = 0x00)
      )
  | Function_symbol(_) ->
      let opcode = Char.code str.[byte - 3] in
      (* False positive: rlwinm with bitmask 0 31 = bitmask n (n - 1) *)
      not (0x54 <= opcode && opcode <= 0x57 && old = 0x3E
          && (fuz = 0x40 || fuz = 0x82 || fuz = 0xc4))
  | Zero_symbol        -> false
  | Stub(_)            -> true
  | Jumptable          -> true
  | Float_literal(_)   ->
      (* False positive: 0. becomes -0. *)
      not (
        (byte = a)
        && (fuz = 0x80)
        && (Char.code str.[byte + 0] = 0x00)
        && (Char.code str.[byte + 1] = 0x00)
        && (Char.code str.[byte + 2] = 0x00)
        && (Char.code str.[byte + 3] = 0x00)
        && (Char.code str.[byte + 4] = 0x00)
        && (Char.code str.[byte + 5] = 0x00)
        && (Char.code str.[byte + 6] = 0x00)
        && (Char.code str.[byte + 7] = 0x00)
      )
  | Padding            -> false (* padding may be non-null *)
  | Unknown(_)         -> false

let fuzz_byte str byte_ndx =
  let rand = Char.chr (Random.int 255) in (* [0 - 254] *)
  if rand = str.[byte_ndx] (* if we picked a byte equal to the current *)
  then Char.chr 255        (* then replace with byte 255 *)
  else rand                (* else replace with the byte we picked *)

let rec find_byte_to_fuzz elfmap str byterange =
  let byte = Random.int byterange in
  let fuzz = fuzz_byte str byte in
  if ok_fuzz elfmap str byte fuzz
  then (byte, fuzz)
  else find_byte_to_fuzz elfmap str byterange

let get_elfmap elffilename =
  let ic = open_in (elffilename ^ ".elfmap") in
  let elfmap = input_value ic in
  close_in ic;
  elfmap

let fuzz_loop elffilename sdumps =
  let elfmap = get_elfmap elffilename in
  let (str, ofs, len) = Bitstring.bitstring_of_file elffilename in
  let rec fuzz_loop_aux () =
    let (byte, fuzz) = find_byte_to_fuzz elfmap str (len/8) in
    let str' = String.copy str in
    str'.[byte] <- fuzz;
    fuzz_check elfmap (str', ofs, len) byte str.[byte] sdumps;
    fuzz_loop_aux ()
  in fuzz_loop_aux ()
(*
  let fuzz_all elffilename sdumps =
  let elfmap = get_elfmap elffilename in
  let (str, ofs, len) = Bitstring.bitstring_of_file elffilename in
  let rec fuzz_all_aux current limit =
  if current = limit
  then ()
  else (
  if ok_fuzz elfmap current
  then (
  let str' = String.copy str in
  fuzz_byte str' current;
  let msg = string_of_int32 (int_int32 current) ^ " <- " ^
  string_of_byte (Char.code str'.[current]) ^ " (was " ^
  string_of_byte (Char.code str.[current]) ^ ") - " ^
  string_of_elf_range (range_of_byte elfmap current)
  in
  fuzz_check msg (str', ofs, len) sdumps
  );
  fuzz_all_aux (current + 1) limit
  )
  in fuzz_all_aux 0 (len/8)
*)