3 # Copyright (C) 2013 Oracle.
5 # Licensed under the Open Software License version 1.1
13 con
= sqlite3
.connect('smatch_db.sqlite')
14 except sqlite3
.Error
as e
:
15 print("Error %s:" % e
.args
[0])
19 print("%s" % (sys
.argv
[0]))
20 print("<function> - how a function is called")
21 print("info <type> - how a function is called, filtered by type")
22 print("return_states <function> - what a function returns")
23 print("call_tree <function> - show the call tree")
24 print("where <struct_type> <member> - where a struct member is set")
25 print("type_size <struct_type> <member> - how a struct member is allocated")
26 print("data_info <struct_type> <member> - information about a given data type")
27 print("function_ptr <function> - which function pointers point to this")
28 print("trace_param <function> <param> - trace where a parameter came from")
29 print("find_tagged <function> <param> - find the source of a tagged value (arm64)")
30 print("parse_warns_tagged <smatch_warns.txt> - parse warns file for summary of tagged issues (arm64)")
31 print("locals <file> - print the local values in a file.")
36 def get_function_pointers_helper(func
):
38 cur
.execute("select distinct ptr from function_ptr where function = '%s';" %(func))
41 if ptr
in function_ptrs
:
43 function_ptrs
.append(ptr
)
44 if not ptr
in searched_ptrs
:
45 searched_ptrs
.append(ptr
)
46 get_function_pointers_helper(ptr
)
48 def get_function_pointers(func
):
51 function_ptrs
= [func
]
52 searched_ptrs
= [func
]
53 get_function_pointers_helper(func
)
56 db_types
= { 0: "INTERNAL",
67 1009: "LOCK_RELEASED",
68 1010: "ABSOLUTE_LIMITS",
79 1023: "UNTRACKED_PARAM",
84 1028: "COMPARE_LIMIT",
85 1029: "PARAM_COMPARE",
89 1033: "CONSTRAINT_REQUIRED",
106 9017: "USER_DATA_SET",
108 9019: "USER_PTR_SET",
110 8019: "NO_OVERFLOW_SIMPLE",
115 9023: "LOCK_RESTORED",
116 9024: "KNOWN_LOCKED",
117 9025: "KNOWN_UNLOCKED",
120 9027: "REFCOUNT_INC",
121 9028: "REFCOUNT_DEC",
124 def add_range(rl
, min_val
, max_val
):
131 return [[min_val
, max_val
]]
133 for idx
in range(len(rl
)):
137 # we already merged the new range but we might need to change later
138 # ranges if they over lap with more than one
140 # join with added range
141 if max_val
+ 1 == cur_min
:
142 ret
[len(ret
) - 1][1] = cur_max
146 if max_val
< cur_min
:
147 ret
.append([cur_min
, cur_max
])
151 if max_val
< cur_max
:
152 ret
[len(ret
) - 1][1] = cur_max
158 # join 2 ranges into one
159 if max_val
+ 1 == cur_min
:
160 ret
.append([min_val
, cur_max
])
163 # range is entirely below
164 if max_val
< cur_min
:
165 ret
.append([min_val
, max_val
])
166 ret
.append([cur_min
, cur_max
])
169 # range is partially below
170 if min_val
< cur_min
:
171 if max_val
<= cur_max
:
172 ret
.append([min_val
, cur_max
])
176 ret
.append([min_val
, max_val
])
179 # range already included
180 if max_val
<= cur_max
:
181 ret
.append([cur_min
, cur_max
])
184 # range partially above
185 if min_val
<= cur_max
:
186 ret
.append([cur_min
, max_val
])
189 # join 2 ranges on the other side
190 if min_val
- 1 == cur_max
:
191 ret
.append([cur_min
, max_val
])
195 ret
.append([cur_min
, cur_max
])
197 if idx
+ 1 < len(rl
): # we hit a break statement
198 ret
= ret
+ rl
[idx
+ 1:]
199 elif done
: # we hit a break on the last iteration
201 elif not check_next
: # it's past the end of the rl
202 ret
.append([min_val
, max_val
])
206 def rl_union(rl1
, rl2
):
209 ret
= add_range(ret
, r
[0], r
[1])
211 ret
= add_range(ret
, r
[0], r
[1])
213 if (rl1
or rl2
) and not ret
:
214 print("bug: merging %s + %s gives empty" %(rl1
, rl2
))
221 elif txt
== "s32min":
223 elif txt
== "s16min":
225 elif txt
== "s64max":
227 elif txt
== "s32max":
229 elif txt
== "s16max":
231 elif txt
== "u64max":
233 elif txt
== "ptr_max":
235 elif txt
== "u32max":
237 elif txt
== "u16max":
248 elif val
== -(2**31):
250 elif val
== -(2**15):
252 elif val
== 2**63 - 1:
254 elif val
== 2**31 - 1:
256 elif val
== 2**15 - 1:
258 elif val
== 2**64 - 1:
260 elif val
== 2**32 - 1:
262 elif val
== 2**16 - 1:
269 def get_next_str(txt
):
281 elif txt
[0] == 's' or txt
[0] == 'u':
287 for char
in txt
[parsed
:]:
288 if char
== '-' or char
== '[':
299 pairs
= txt
.split(",")
301 cnt
, min_str
= get_next_str(pair
)
305 cnt
, max_str
= get_next_str(pair
[cnt
+ 1:])
306 min_val
= txt_to_val(min_str
)
307 max_val
= txt_to_val(max_str
)
308 ret
.append([min_val
, max_val
])
310 # Hm... Smatch won't call INT_MAX s32max if the variable is unsigned.
311 # if txt != rl_to_txt(ret):
312 # print("bug: converting: text = %s rl = %s internal = %s" %(txt, rl_to_txt(ret), ret))
318 for idx
in range(len(rl
)):
325 if cur_min
== cur_max
:
326 ret
+= val_to_txt(cur_min
)
328 ret
+= val_to_txt(cur_min
)
330 ret
+= val_to_txt(cur_max
)
333 def type_to_str(type_int
):
340 def type_to_int(type_string
):
341 for k
in db_types
.keys():
342 if db_types
[k
] == type_string
:
346 def display_caller_info(printed
, cur
, param_names
):
349 print("file | caller | function | type | parameter | key | value |")
352 parameter
= int(txt
[6])
354 if len(param_names
) and parameter
in param_names
:
355 key
= key
.replace("$", param_names
[parameter
])
357 print("%20s | %20s | %20s |" %(txt
[0], txt
[1], txt
[2]), end
= '')
358 print(" %18s |" %(type_to_str(txt
[5])), end
= '')
359 print(" %2d | %15s | %s" %(parameter
, key
, txt
[8]))
362 def get_caller_info(filename
, ptrs
, my_type
):
364 param_names
= get_param_names(filename
, func
)
368 type_filter
= "and type = %d" %(type_to_int(my_type
))
370 cur
.execute("select * from caller_info where function = '%s' %s;" %(ptr
, type_filter
))
371 printed
= display_caller_info(printed
, cur
, param_names
)
373 def print_caller_info(filename
, func
, my_type
= ""):
374 ptrs
= get_function_pointers(func
)
375 get_caller_info(filename
, ptrs
, my_type
)
377 def merge_values(param_names
, vals
, cur
):
379 parameter
= int(txt
[0])
381 rl
= txt_to_rl(txt
[2])
382 if parameter
in param_names
:
383 name
= name
.replace("$", param_names
[parameter
])
385 if not parameter
in vals
:
388 # the first item on the list is the number of rows. it's incremented
389 # every time we call merge_values().
390 if name
in vals
[parameter
]:
391 vals
[parameter
][name
] = [vals
[parameter
][name
][0] + 1, rl_union(vals
[parameter
][name
][1], rl
)]
393 vals
[parameter
][name
] = [1, rl
]
395 def get_param_names(filename
, func
):
398 cur
.execute("select parameter, value from parameter_name where file = '%s' and function = '%s';" %(filename
, func
))
400 parameter
= int(txt
[0])
402 param_names
[parameter
] = name
406 cur
.execute("select parameter, value from parameter_name where function = '%s';" %(func))
408 parameter
= int(txt
[0])
410 param_names
[parameter
] = name
413 def get_caller_count(ptrs
):
417 cur
.execute("select count(distinct(call_id)) from caller_info where function = '%s';" %(ptr))
422 def print_merged_caller_values(filename
, func
, ptrs
, param_names
, call_cnt
):
426 cur
.execute("select parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr
, type_to_int("PARAM_VALUE")))
427 merge_values(param_names
, vals
, cur
);
429 for param
in sorted(vals
):
430 for name
in sorted(vals
[param
]):
431 if vals
[param
][name
][0] != call_cnt
:
433 print("%d %s -> %s" %(param
, name
, rl_to_txt(vals
[param
][name
][1])))
436 def print_unmerged_caller_values(filename
, func
, ptrs
, param_names
):
440 cur
.execute("select file, caller, call_id, parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr
, type_to_int("PARAM_VALUE")))
441 for filename
, caller
, call_id
, parameter
, name
, value
in cur
:
442 if prev
!= int(call_id
):
445 parameter
= int(parameter
)
446 if parameter
< len(param_names
):
447 name
= name
.replace("$", param_names
[parameter
])
449 name
= name
.replace("$", "$%d" %(parameter))
451 print("%s | %s | %s | %s" %(filename
, caller
, name
, value
))
452 print("==========================")
454 def print_caller_values(filename
, func
, ptrs
):
455 param_names
= get_param_names(filename
, func
)
456 call_cnt
= get_caller_count(ptrs
)
458 print_merged_caller_values(filename
, func
, ptrs
, param_names
, call_cnt
)
459 print("==========================")
460 print_unmerged_caller_values(filename
, func
, ptrs
, param_names
)
462 def caller_info_values(filename
, func
):
463 ptrs
= get_function_pointers(func
)
464 print_caller_values(filename
, func
, ptrs
)
466 def print_return_states(func
):
468 cur
.execute("select * from return_states where function = '%s';" %(func))
474 print("file | function | return_id | return_value | type | param | key | value |")
476 print("%s | %s | %2s | %13s" %(txt
[0], txt
[1], txt
[3], txt
[4]), end
= '')
477 print("| %15s |" %(type_to_str(txt
[6])), end
= '')
478 print(" %2d | %20s | %20s |" %(txt
[7], txt
[8], txt
[9]))
480 print("\n<ERROR parsing: 'select * from return_states where function = '%s';'>\n" %(func))
482 def print_return_implies(func
):
484 cur
.execute("select * from return_implies where function = '%s';" %(func))
488 print("file | function | type | param | key | value |")
490 print("%15s | %15s" %(txt
[0], txt
[1]), end
= '')
491 print("| %15s" %(type_to_str(txt
[4])), end
= '')
492 print("| %3d | %15s | %15s |" %(txt
[5], txt
[6], txt
[7]))
494 def print_type_size(struct_type
, member
):
496 cur
.execute("select * from type_size where type like '(struct %s)->%s';" %(struct_type
, member
))
499 print("%-15s | %s" %(txt
[0], txt
[1]))
501 cur
.execute("select * from function_type_size where type like '(struct %s)->%s';" %(struct_type
, member
))
502 print("file | function | type | size")
504 print("%-15s | %-15s | %-15s | %s" %(txt
[0], txt
[1], txt
[2], txt
[3]))
506 def print_data_info(struct_type
, member
):
508 cur
.execute("select * from data_info where data like '(struct %s)->%s';" %(struct_type
, member
))
509 print("file | data | type | value")
511 print("%-15s | %-15s | %-15s | %s" %(txt
[0], txt
[1], type_to_str(txt
[2]), txt
[3]))
513 def print_fn_ptrs(func
):
514 ptrs
= get_function_pointers(func
)
517 print("%s = " %(func), end
= '')
520 def print_functions(struct
, member
):
523 cur
.execute("select * from function_ptr where ptr like '(struct %s)->%s';" %(struct
, member
))
524 elif member
.find(" ") >= 0:
525 cur
.execute("select * from function_ptr where ptr = '%s';" %(member))
527 cur
.execute("select * from function_ptr where ptr like '%%->%s';" %(member))
528 print("File | Pointer | Function | Static")
530 print("%-15s | %-15s | %-15s | %s" %(txt
[0], txt
[2], txt
[1], txt
[3]))
532 def get_callers(func
, restrict
= ""):
534 restrict
= "and type = 0"
537 ptrs
= get_function_pointers(func
)
539 cur
.execute("select distinct caller from caller_info where function = '%s' %s;" %(ptr
, restrict
))
545 def call_tree_helper(func
, restrict
= "", indent
= 0):
547 if func
in printed_funcs
:
549 print("%s%s()" %(" " * indent
, func
))
550 if func
== "too common":
554 printed_funcs
.append(func
)
555 callers
= get_callers(func
, restrict
)
556 if len(callers
) >= 20:
557 print("Over 20 callers for %s()" %(func))
559 for caller
in callers
:
560 call_tree_helper(caller
, restrict
, indent
+ 2)
562 def print_call_tree(func
):
565 call_tree_helper(func
)
567 def print_preempt_tree(func
):
570 call_tree_helper(func
, "and type = 2054")
572 def function_type_value(struct_type
, member
):
574 cur
.execute("select * from function_type_value where type like '(struct %s)->%s';" %(struct_type
, member
))
576 print("%-30s | %-30s | %s | %s" %(txt
[0], txt
[1], txt
[2], txt
[3]))
581 for idx
in range(len(rl
)):
583 if (cur_max
> 0xFFFFFFFFFFFFFF):
588 def rl_has_min_untagged(txt
):
591 for idx
in range(len(rl
)):
593 if (cur_min
== 0xff80000000000000):
598 def rl_is_tagged(txt
):
599 if not rl_too_big(txt
):
602 if rl_has_min_untagged(txt
):
607 def rl_is_treat_untagged(txt
):
613 def parse_warns_tagged(filename
):
614 proc
= subprocess
.Popen(['cat %s | grep "potentially tagged" | sort | uniq' %(filename)], shell
=True, stdout
=subprocess
.PIPE
)
616 line
= proc
.stdout
.readline()
620 linepos
= re
.search("([^\s]+)", line
).group(1)
621 groupre
= re
.search("potentially tagged address \(([^,]+), ([^,]+), ([^\)]+)\)", line
)
624 func
= groupre
.group(1)
625 param
= int(groupre
.group(2))
626 var
= groupre
.group(3)
628 if ("end" in var
or "size" in var
or "len" in var
):
631 print("\n%s (func: %s, param: %d:%s) may be caused by:" %(linepos
, func
, param
, var
))
634 if not find_tagged(func
, param
, 0, []):
635 print(" %s (param %d) (can't walk call tree)" % (func
, param
))
637 print(" %s (variable %s (can't walk call tree)" % (func
, var
))
639 def find_tagged(func
, param
, caller_call_id
, printed
):
643 ptrs
= get_function_pointers(func
)
647 cur
.execute("select call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr
, param
, type_to_int("DATA_SOURCE")))
650 if (row
[1][0] == '$'):
651 if row
[0] not in callers
:
653 callers
[row
[0]]["param"] = int(row
[1][1])
656 cur
.execute("select caller, call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr
, param
, type_to_int("USER_DATA")))
659 if not rl_is_tagged(row
[2]):
661 if rl_is_treat_untagged(row
[2]):
664 if row
[1] not in callers
:
666 if "param" not in callers
[row
[1]]:
667 line
= " %s (param ?) -> %s (param %d)" % (row
[0], func
, param
)
668 if line
not in printed
:
672 if row
[0] not in printed
:
673 printed
.append(row
[0])
674 if not find_tagged(row
[0], callers
[row
[1]]["param"], row
[1], printed
):
675 print(" %s (param %d)" % (row
[0], param
))
679 def trace_callers(func
, param
):
684 ptrs
= get_function_pointers(func
)
686 cur
.execute("select type, caller, value from caller_info where function = '%s' and (type = 0 or type = 1014 or type = 1028) and (parameter = -1 or parameter = %d);" %(ptr
, param
))
688 data_type
= int(row
[0])
689 if data_type
== 1014:
690 sources
.append((row
[1], row
[2]))
691 elif data_type
== 1028:
692 sources
.append(("%", row
[2])) # hack...
693 elif data_type
== 0 and prev_type
== 0:
694 sources
.append((row
[1], ""))
695 prev_type
= data_type
698 def trace_param_helper(func
, param
, indent
= 0):
700 if func
in printed_funcs
:
702 print("%s%s(param %d)" %(" " * indent
, func
, param
))
703 if func
== "too common":
707 printed_funcs
.append(func
)
708 sources
= trace_callers(func
, param
)
711 if len(path
[1]) and path
[1][0] == '$':
712 p
= int(re
.findall('\d+', path
[1][1:])[0])
713 trace_param_helper(path
[0], p
, indent
+ 2)
714 elif len(path
[0]) and path
[0][0] == '%':
715 print(" %s%s" %(" " * indent
, path
[1]))
717 print("* %s%s %s" %(" " * (indent
- 1), path
[0], path
[1]))
719 def trace_param(func
, param
):
722 print("tracing %s %d" %(func
, param
))
723 trace_param_helper(func
, param
)
725 def print_locals(filename
):
727 cur
.execute("select file,data,value from data_info where file = '%s' and type = 8029 and value != 0;" %(filename))
729 print("%s | %s | %s" %(txt
[0], txt
[1], txt
[2]))
731 def constraint(struct_type
, member
):
733 cur
.execute("select * from constraints_required where data like '(struct %s)->%s' or bound like '(struct %s)->%s';" %(struct_type
, member
, struct_type
, member
))
735 print("%-30s | %-30s | %s | %s" %(txt
[0], txt
[1], txt
[2], txt
[3]))
737 if len(sys
.argv
) < 2:
740 if len(sys
.argv
) == 2:
742 print_caller_info("", func
)
743 elif sys
.argv
[1] == "info":
745 if len(sys
.argv
) == 4:
746 my_type
= sys
.argv
[3]
748 print_caller_info("", func
, my_type
)
749 elif sys
.argv
[1] == "call_info":
750 if len(sys
.argv
) != 4:
752 filename
= sys
.argv
[2]
754 caller_info_values(filename
, func
)
755 print_caller_info(filename
, func
)
756 elif sys
.argv
[1] == "function_ptr" or sys
.argv
[1] == "fn_ptr":
759 elif sys
.argv
[1] == "return_states":
761 print_return_states(func
)
762 print("================================================")
763 print_return_implies(func
)
764 elif sys
.argv
[1] == "return_implies":
766 print_return_implies(func
)
767 elif sys
.argv
[1] == "type_size" or sys
.argv
[1] == "buf_size":
768 struct_type
= sys
.argv
[2]
770 print_type_size(struct_type
, member
)
771 elif sys
.argv
[1] == "data_info":
772 struct_type
= sys
.argv
[2]
774 print_data_info(struct_type
, member
)
775 elif sys
.argv
[1] == "call_tree":
777 print_call_tree(func
)
778 elif sys
.argv
[1] == "preempt":
780 print_preempt_tree(func
)
781 elif sys
.argv
[1] == "find_tagged":
783 param
= int(sys
.argv
[3])
784 find_tagged(func
, param
, 0, [])
785 elif sys
.argv
[1] == "parse_warns_tagged":
786 filename
= sys
.argv
[2]
787 parse_warns_tagged(filename
)
788 elif sys
.argv
[1] == "where":
789 if len(sys
.argv
) == 3:
792 elif len(sys
.argv
) == 4:
793 struct_type
= sys
.argv
[2]
795 function_type_value(struct_type
, member
)
796 elif sys
.argv
[1] == "local":
797 filename
= sys
.argv
[2]
799 if len(sys
.argv
) == 4:
800 variable
= sys
.argv
[3]
801 local_values(filename
, variable
)
802 elif sys
.argv
[1] == "functions":
803 if len(sys
.argv
) == 4:
809 print_functions(struct
, member
)
810 elif sys
.argv
[1] == "trace_param":
811 if len(sys
.argv
) != 4:
814 param
= int(sys
.argv
[3])
815 trace_param(func
, param
)
816 elif sys
.argv
[1] == "locals":
817 if len(sys
.argv
) != 3:
819 filename
= sys
.argv
[2]
820 print_locals(filename
);
821 elif sys
.argv
[1] == "constraint":
822 if len(sys
.argv
) == 3:
825 elif len(sys
.argv
) == 4:
826 struct_type
= sys
.argv
[2]
828 constraint(struct_type
, member
)