11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2017 Oracle.  All rights reserved.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
181f5207b7SJohn Levon /*
191f5207b7SJohn Levon  * One problem that I have is that it's really hard to track how pointers are
201f5207b7SJohn Levon  * passed around.  For example, it would be nice to know that the probe() and
211f5207b7SJohn Levon  * remove() functions get the same pci_dev pointer.  It would be good to know
221f5207b7SJohn Levon  * what pointers we're passing to the open() and close() functions.  But that
231f5207b7SJohn Levon  * information gets lost in a call tree full of function pointer calls.
241f5207b7SJohn Levon  *
251f5207b7SJohn Levon  * I think the first step is to start naming specific pointers.  So when a
261f5207b7SJohn Levon  * pointer is allocated, then it gets a tag.  So calls to kmalloc() generate a
271f5207b7SJohn Levon  * tag.  But we might not use that, because there might be a better name like
281f5207b7SJohn Levon  * framebuffer_alloc(). The framebuffer_alloc() is interesting because there is
291f5207b7SJohn Levon  * one per driver and it's passed around to all the file operations.
301f5207b7SJohn Levon  *
311f5207b7SJohn Levon  * Perhaps we could make a list of functions like framebuffer_alloc() which take
321f5207b7SJohn Levon  * a size and say that those are the interesting alloc functions.
331f5207b7SJohn Levon  *
341f5207b7SJohn Levon  * Another place where we would maybe name the pointer is when they are passed
351f5207b7SJohn Levon  * to the probe().  Because that's an important pointer, since there is one
361f5207b7SJohn Levon  * per driver (sort of).
371f5207b7SJohn Levon  *
381f5207b7SJohn Levon  * My vision is that you could take a pointer and trace it back to a global.  So
391f5207b7SJohn Levon  * I'm going to track that pointer_tag - 28 bytes takes you to another pointer
401f5207b7SJohn Levon  * tag.  You could follow that one back and so on.  Also when we pass a pointer
411f5207b7SJohn Levon  * to a function that would be recorded as sort of a link or path or something.
421f5207b7SJohn Levon  *
431f5207b7SJohn Levon  */
441f5207b7SJohn Levon 
451f5207b7SJohn Levon #include "smatch.h"
461f5207b7SJohn Levon #include "smatch_slist.h"
471f5207b7SJohn Levon #include "smatch_extra.h"
481f5207b7SJohn Levon 
49*6ff4183cSAndy Fiddaman #include <md5.h>
501f5207b7SJohn Levon 
511f5207b7SJohn Levon static int my_id;
521f5207b7SJohn Levon 
str_to_mtag(const char * str)5331ad075eSJohn Levon mtag_t str_to_mtag(const char *str)
541f5207b7SJohn Levon {
551f5207b7SJohn Levon 	unsigned char c[MD5_DIGEST_LENGTH];
561f5207b7SJohn Levon 	unsigned long long *tag = (unsigned long long *)&c;
571f5207b7SJohn Levon 	int len;
581f5207b7SJohn Levon 
591f5207b7SJohn Levon 	len = strlen(str);
60*6ff4183cSAndy Fiddaman 	md5_calc(c, str, len);
611f5207b7SJohn Levon 
621f5207b7SJohn Levon 	*tag &= ~MTAG_ALIAS_BIT;
631f5207b7SJohn Levon 	*tag &= ~MTAG_OFFSET_MASK;
641f5207b7SJohn Levon 
651f5207b7SJohn Levon 	return *tag;
661f5207b7SJohn Levon }
671f5207b7SJohn Levon 
save_allocator(void * _allocator,int argc,char ** argv,char ** azColName)686523a3aaSJohn Levon static int save_allocator(void *_allocator, int argc, char **argv, char **azColName)
696523a3aaSJohn Levon {
706523a3aaSJohn Levon 	char **allocator = _allocator;
716523a3aaSJohn Levon 
726523a3aaSJohn Levon 	if (*allocator) {
736523a3aaSJohn Levon 		if (strcmp(*allocator, argv[0]) == 0)
746523a3aaSJohn Levon 			return 0;
756523a3aaSJohn Levon 		/* should be impossible */
766523a3aaSJohn Levon 		free_string(*allocator);
776523a3aaSJohn Levon 		*allocator = alloc_string("unknown");
786523a3aaSJohn Levon 		return 0;
796523a3aaSJohn Levon 	}
806523a3aaSJohn Levon 	*allocator = alloc_string(argv[0]);
816523a3aaSJohn Levon 	return 0;
826523a3aaSJohn Levon }
836523a3aaSJohn Levon 
get_allocator_info_from_tag(mtag_t tag)846523a3aaSJohn Levon char *get_allocator_info_from_tag(mtag_t tag)
856523a3aaSJohn Levon {
866523a3aaSJohn Levon 	char *allocator = NULL;
876523a3aaSJohn Levon 
886523a3aaSJohn Levon 	run_sql(save_allocator, &allocator,
896523a3aaSJohn Levon 		"select value from mtag_info where tag = %lld and type = %d;",
906523a3aaSJohn Levon 		tag, ALLOCATOR);
916523a3aaSJohn Levon 
926523a3aaSJohn Levon 	return allocator;
936523a3aaSJohn Levon }
946523a3aaSJohn Levon 
get_allocator_info(struct expression * expr,struct smatch_state * state)956523a3aaSJohn Levon static char *get_allocator_info(struct expression *expr, struct smatch_state *state)
96efe51d0cSJohn Levon {
97efe51d0cSJohn Levon 	sval_t sval;
98efe51d0cSJohn Levon 
996523a3aaSJohn Levon 	if (expr->type != EXPR_ASSIGNMENT)
1006523a3aaSJohn Levon 		return NULL;
1016523a3aaSJohn Levon 	if (estate_get_single_value(state, &sval))
1026523a3aaSJohn Levon 		return get_allocator_info_from_tag(sval.value);
1036523a3aaSJohn Levon 
1046523a3aaSJohn Levon 	expr = strip_expr(expr->right);
105efe51d0cSJohn Levon 	if (expr->type != EXPR_CALL ||
1066523a3aaSJohn Levon 	    !expr->fn ||
1076523a3aaSJohn Levon 	    expr->fn->type != EXPR_SYMBOL)
1086523a3aaSJohn Levon 		return NULL;
1096523a3aaSJohn Levon 	return expr_to_str(expr->fn);
1106523a3aaSJohn Levon }
111efe51d0cSJohn Levon 
update_mtag_info(struct expression * expr,mtag_t tag,const char * left_name,const char * tag_info,struct smatch_state * state)1126523a3aaSJohn Levon static void update_mtag_info(struct expression *expr, mtag_t tag,
1136523a3aaSJohn Levon 			     const char *left_name, const char *tag_info,
1146523a3aaSJohn Levon 			     struct smatch_state *state)
1156523a3aaSJohn Levon {
1166523a3aaSJohn Levon 	char *allocator;
117efe51d0cSJohn Levon 
1186523a3aaSJohn Levon 	sql_insert_mtag_about(tag, left_name, tag_info);
119efe51d0cSJohn Levon 
1206523a3aaSJohn Levon 	allocator = get_allocator_info(expr, state);
1216523a3aaSJohn Levon 	if (allocator)
1226523a3aaSJohn Levon 		sql_insert_mtag_info(tag, ALLOCATOR, allocator);
123efe51d0cSJohn Levon }
124efe51d0cSJohn Levon 
get_mtag_return(struct expression * expr,struct smatch_state * state)1256523a3aaSJohn Levon struct smatch_state *get_mtag_return(struct expression *expr, struct smatch_state *state)
1261f5207b7SJohn Levon {
1271f5207b7SJohn Levon 	struct expression *left, *right;
1281f5207b7SJohn Levon 	char *left_name, *right_name;
1291f5207b7SJohn Levon 	struct symbol *left_sym;
130efe51d0cSJohn Levon 	struct range_list *rl;
1311f5207b7SJohn Levon 	char buf[256];
1321f5207b7SJohn Levon 	mtag_t tag;
133efe51d0cSJohn Levon 	sval_t tag_sval;
1341f5207b7SJohn Levon 
135efe51d0cSJohn Levon 	if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=')
1366523a3aaSJohn Levon 		return NULL;
1376523a3aaSJohn Levon 	if (!is_fresh_alloc(expr->right))
1386523a3aaSJohn Levon 		return NULL;
1396523a3aaSJohn Levon 	if (!rl_intersection(estate_rl(state), valid_ptr_rl))
1406523a3aaSJohn Levon 		return NULL;
1411f5207b7SJohn Levon 
1421f5207b7SJohn Levon 	left = strip_expr(expr->left);
1431f5207b7SJohn Levon 	right = strip_expr(expr->right);
144efe51d0cSJohn Levon 
1451f5207b7SJohn Levon 	left_name = expr_to_str_sym(left, &left_sym);
146efe51d0cSJohn Levon 	if (!left_name || !left_sym)
1476523a3aaSJohn Levon 		return NULL;
1481f5207b7SJohn Levon 	right_name = expr_to_str(right);
1491f5207b7SJohn Levon 
1501f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
1511f5207b7SJohn Levon 		 left_name, right_name);
15231ad075eSJohn Levon 	tag = str_to_mtag(buf);
153efe51d0cSJohn Levon 	tag_sval.type = estate_type(state);
154efe51d0cSJohn Levon 	tag_sval.uvalue = tag;
1551f5207b7SJohn Levon 
156efe51d0cSJohn Levon 	rl = rl_filter(estate_rl(state), valid_ptr_rl);
157efe51d0cSJohn Levon 	rl = clone_rl(rl);
158efe51d0cSJohn Levon 	add_range(&rl, tag_sval, tag_sval);
1591f5207b7SJohn Levon 
1606523a3aaSJohn Levon 	update_mtag_info(expr, tag, left_name, buf, state);
1611f5207b7SJohn Levon 
1621f5207b7SJohn Levon 	free_string(left_name);
1631f5207b7SJohn Levon 	free_string(right_name);
164efe51d0cSJohn Levon 
165efe51d0cSJohn Levon 	return alloc_estate_rl(rl);
1661f5207b7SJohn Levon }
1671f5207b7SJohn Levon 
get_string_mtag(struct expression * expr,mtag_t * tag)1681f5207b7SJohn Levon int get_string_mtag(struct expression *expr, mtag_t *tag)
1691f5207b7SJohn Levon {
1701f5207b7SJohn Levon 	mtag_t xor;
1711f5207b7SJohn Levon 
1721f5207b7SJohn Levon 	if (expr->type != EXPR_STRING || !expr->string)
1731f5207b7SJohn Levon 		return 0;
1741f5207b7SJohn Levon 
1751f5207b7SJohn Levon 	/* I was worried about collisions so I added a xor */
17631ad075eSJohn Levon 	xor = str_to_mtag("__smatch string");
17731ad075eSJohn Levon 	*tag = str_to_mtag(expr->string->data);
1781f5207b7SJohn Levon 	*tag = *tag ^ xor;
1791f5207b7SJohn Levon 
1801f5207b7SJohn Levon 	return 1;
1811f5207b7SJohn Levon }
1821f5207b7SJohn Levon 
get_toplevel_mtag(struct symbol * sym,mtag_t * tag)1831f5207b7SJohn Levon int get_toplevel_mtag(struct symbol *sym, mtag_t *tag)
1841f5207b7SJohn Levon {
1851f5207b7SJohn Levon 	char buf[256];
1861f5207b7SJohn Levon 
1871f5207b7SJohn Levon 	if (!sym)
1881f5207b7SJohn Levon 		return 0;
1891f5207b7SJohn Levon 
1901f5207b7SJohn Levon 	if (!sym->ident ||
1911f5207b7SJohn Levon 	    !(sym->ctype.modifiers & MOD_TOPLEVEL))
1921f5207b7SJohn Levon 		return 0;
1931f5207b7SJohn Levon 
1941f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%s %s",
1951f5207b7SJohn Levon 		 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern",
1961f5207b7SJohn Levon 		 sym->ident->name);
19731ad075eSJohn Levon 	*tag = str_to_mtag(buf);
1981f5207b7SJohn Levon 	return 1;
1991f5207b7SJohn Levon }
2001f5207b7SJohn Levon 
get_symbol_mtag(struct symbol * sym,mtag_t * tag)201efe51d0cSJohn Levon bool get_symbol_mtag(struct symbol *sym, mtag_t *tag)
2021f5207b7SJohn Levon {
203efe51d0cSJohn Levon 	char buf[256];
2041f5207b7SJohn Levon 
205efe51d0cSJohn Levon 	if (!sym || !sym->ident)
206efe51d0cSJohn Levon 		return false;
2071f5207b7SJohn Levon 
208efe51d0cSJohn Levon 	if (get_toplevel_mtag(sym, tag))
209efe51d0cSJohn Levon 		return true;
2101f5207b7SJohn Levon 
211efe51d0cSJohn Levon 	if (get_param_num_from_sym(sym) >= 0)
212efe51d0cSJohn Levon 		return false;
2131f5207b7SJohn Levon 
214efe51d0cSJohn Levon 	snprintf(buf, sizeof(buf), "%s %s %s",
215efe51d0cSJohn Levon 		 get_filename(), get_function(), sym->ident->name);
21631ad075eSJohn Levon 	*tag = str_to_mtag(buf);
217efe51d0cSJohn Levon 	return true;
2181f5207b7SJohn Levon }
2191f5207b7SJohn Levon 
global_variable(struct symbol * sym)2201f5207b7SJohn Levon static void global_variable(struct symbol *sym)
2211f5207b7SJohn Levon {
2221f5207b7SJohn Levon 	mtag_t tag;
2231f5207b7SJohn Levon 
2241f5207b7SJohn Levon 	if (!get_toplevel_mtag(sym, &tag))
2251f5207b7SJohn Levon 		return;
2261f5207b7SJohn Levon 
2271f5207b7SJohn Levon 	sql_insert_mtag_about(tag,
2281f5207b7SJohn Levon 			      sym->ident->name,
2291f5207b7SJohn Levon 			      (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
2301f5207b7SJohn Levon }
2311f5207b7SJohn Levon 
get_array_mtag_offset(struct expression * expr,mtag_t * tag,int * offset)2321f5207b7SJohn Levon static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
2331f5207b7SJohn Levon {
2341f5207b7SJohn Levon 	struct expression *array, *offset_expr;
2351f5207b7SJohn Levon 	struct symbol *type;
2361f5207b7SJohn Levon 	sval_t sval;
237efe51d0cSJohn Levon 	int start_offset;
2381f5207b7SJohn Levon 
2391f5207b7SJohn Levon 	if (!is_array(expr))
2401f5207b7SJohn Levon 		return 0;
2411f5207b7SJohn Levon 
2421f5207b7SJohn Levon 	array = get_array_base(expr);
2431f5207b7SJohn Levon 	if (!array)
2441f5207b7SJohn Levon 		return 0;
2451f5207b7SJohn Levon 	type = get_type(array);
2461f5207b7SJohn Levon 	if (!type || type->type != SYM_ARRAY)
2471f5207b7SJohn Levon 		return 0;
2481f5207b7SJohn Levon 	type = get_real_base_type(type);
2491f5207b7SJohn Levon 	if (!type_bytes(type))
2501f5207b7SJohn Levon 		return 0;
2511f5207b7SJohn Levon 
252efe51d0cSJohn Levon 	if (!expr_to_mtag_offset(array, tag, &start_offset))
2531f5207b7SJohn Levon 		return 0;
2541f5207b7SJohn Levon 
2551f5207b7SJohn Levon 	offset_expr = get_array_offset(expr);
2561f5207b7SJohn Levon 	if (!get_value(offset_expr, &sval))
2571f5207b7SJohn Levon 		return 0;
258efe51d0cSJohn Levon 	*offset = start_offset + sval.value * type_bytes(type);
2591f5207b7SJohn Levon 
2601f5207b7SJohn Levon 	return 1;
2611f5207b7SJohn Levon }
2621f5207b7SJohn Levon 
swap_mtag_seed(struct expression * expr,struct range_list * rl)263efe51d0cSJohn Levon struct range_list *swap_mtag_seed(struct expression *expr, struct range_list *rl)
2641f5207b7SJohn Levon {
265efe51d0cSJohn Levon 	char buf[256];
266efe51d0cSJohn Levon 	char *name;
2671f5207b7SJohn Levon 	sval_t sval;
268efe51d0cSJohn Levon 	mtag_t tag;
2691f5207b7SJohn Levon 
270efe51d0cSJohn Levon 	if (!rl_to_sval(rl, &sval))
271efe51d0cSJohn Levon 		return rl;
272efe51d0cSJohn Levon 	if (sval.type->type != SYM_PTR || sval.uvalue != MTAG_SEED)
273efe51d0cSJohn Levon 		return rl;
2741f5207b7SJohn Levon 
275efe51d0cSJohn Levon 	name = expr_to_str(expr);
276efe51d0cSJohn Levon 	snprintf(buf, sizeof(buf), "%s %s %s", get_filename(), get_function(), name);
277efe51d0cSJohn Levon 	free_string(name);
27831ad075eSJohn Levon 	tag = str_to_mtag(buf);
279efe51d0cSJohn Levon 	sval.value = tag;
280efe51d0cSJohn Levon 	return alloc_rl(sval, sval);
2811f5207b7SJohn Levon }
2821f5207b7SJohn Levon 
create_mtag_alias(mtag_t tag,struct expression * expr,mtag_t * new)2831f5207b7SJohn Levon int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
2841f5207b7SJohn Levon {
2851f5207b7SJohn Levon 	char buf[256];
2861f5207b7SJohn Levon 	int lines_from_start;
2871f5207b7SJohn Levon 	char *str;
2881f5207b7SJohn Levon 
2891f5207b7SJohn Levon 	/*
2901f5207b7SJohn Levon 	 * We need the alias to be unique.  It's not totally required that it
2911f5207b7SJohn Levon 	 * be the same from one DB build to then next, but it makes debugging
2921f5207b7SJohn Levon 	 * a bit simpler.
2931f5207b7SJohn Levon 	 *
2941f5207b7SJohn Levon 	 */
2951f5207b7SJohn Levon 
2961f5207b7SJohn Levon 	if (!cur_func_sym)
2971f5207b7SJohn Levon 		return 0;
2981f5207b7SJohn Levon 
2991f5207b7SJohn Levon 	lines_from_start = expr->pos.line - cur_func_sym->pos.line;
3001f5207b7SJohn Levon 	str = expr_to_str(expr);
3011f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%lld %d %s", tag, lines_from_start, str);
3021f5207b7SJohn Levon 	free_string(str);
3031f5207b7SJohn Levon 
30431ad075eSJohn Levon 	*new = str_to_mtag(buf);
3051f5207b7SJohn Levon 	sql_insert_mtag_alias(tag, *new);
3061f5207b7SJohn Levon 
3071f5207b7SJohn Levon 	return 1;
3081f5207b7SJohn Levon }
3091f5207b7SJohn Levon 
get_implied_mtag_offset(struct expression * expr,mtag_t * tag,int * offset)310efe51d0cSJohn Levon static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
311efe51d0cSJohn Levon {
312efe51d0cSJohn Levon 	struct smatch_state *state;
313efe51d0cSJohn Levon 	struct symbol *type;
314efe51d0cSJohn Levon 	sval_t sval;
315efe51d0cSJohn Levon 
316efe51d0cSJohn Levon 	type = get_type(expr);
317efe51d0cSJohn Levon 	if (!type_is_ptr(type))
318efe51d0cSJohn Levon 		return 0;
319efe51d0cSJohn Levon 	state = get_extra_state(expr);
320efe51d0cSJohn Levon 	if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
321efe51d0cSJohn Levon 		return 0;
322efe51d0cSJohn Levon 
323efe51d0cSJohn Levon 	*tag = sval.uvalue & ~MTAG_OFFSET_MASK;
324efe51d0cSJohn Levon 	*offset = sval.uvalue & MTAG_OFFSET_MASK;
325efe51d0cSJohn Levon 	return 1;
326efe51d0cSJohn Levon }
327efe51d0cSJohn Levon 
328efe51d0cSJohn Levon /*
329efe51d0cSJohn Levon  * The point of this function is to give you the mtag and the offset so
330efe51d0cSJohn Levon  * you can look up the data in the DB.  It takes an expression.
331efe51d0cSJohn Levon  *
332efe51d0cSJohn Levon  * So say you give it "foo->bar".  Then it would give you the offset of "bar"
333efe51d0cSJohn Levon  * and the implied value of "foo".  Or if you lookup "*foo" then the offset is
334efe51d0cSJohn Levon  * zero and we look up the implied value of "foo.  But if the expression is
335efe51d0cSJohn Levon  * foo, then if "foo" is a global variable, then we get the mtag and the offset
336efe51d0cSJohn Levon  * is zero.  If "foo" is a local variable, then there is nothing to look up in
337efe51d0cSJohn Levon  * the mtag_data table because that's handled by smatch_extra.c to this returns
338efe51d0cSJohn Levon  * false.
339efe51d0cSJohn Levon  *
340efe51d0cSJohn Levon  */
expr_to_mtag_offset(struct expression * expr,mtag_t * tag,int * offset)3411f5207b7SJohn Levon int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
3421f5207b7SJohn Levon {
343efe51d0cSJohn Levon 	*tag = 0;
3441f5207b7SJohn Levon 	*offset = 0;
3451f5207b7SJohn Levon 
346efe51d0cSJohn Levon 	if (bits_in_pointer != 64)
347efe51d0cSJohn Levon 		return 0;
348efe51d0cSJohn Levon 
3491f5207b7SJohn Levon 	expr = strip_expr(expr);
3501f5207b7SJohn Levon 	if (!expr)
3511f5207b7SJohn Levon 		return 0;
3521f5207b7SJohn Levon 
3531f5207b7SJohn Levon 	if (is_array(expr))
3541f5207b7SJohn Levon 		return get_array_mtag_offset(expr, tag, offset);
3551f5207b7SJohn Levon 
356efe51d0cSJohn Levon 	if (expr->type == EXPR_PREOP && expr->op == '*') {
357efe51d0cSJohn Levon 		expr = strip_expr(expr->unop);
358efe51d0cSJohn Levon 		return get_implied_mtag_offset(expr, tag, offset);
359efe51d0cSJohn Levon 	} else if (expr->type == EXPR_DEREF) {
360efe51d0cSJohn Levon 		int tmp, tmp_offset = 0;
361efe51d0cSJohn Levon 
362efe51d0cSJohn Levon 		while (expr->type == EXPR_DEREF) {
363efe51d0cSJohn Levon 			tmp = get_member_offset_from_deref(expr);
364efe51d0cSJohn Levon 			if (tmp < 0)
365efe51d0cSJohn Levon 				return 0;
366efe51d0cSJohn Levon 			tmp_offset += tmp;
367c85f09ccSJohn Levon 			expr = strip_expr(expr->deref);
368efe51d0cSJohn Levon 		}
369efe51d0cSJohn Levon 		*offset = tmp_offset;
370efe51d0cSJohn Levon 		if (expr->type == EXPR_PREOP && expr->op == '*') {
371efe51d0cSJohn Levon 			expr = strip_expr(expr->unop);
372efe51d0cSJohn Levon 
373efe51d0cSJohn Levon 			if (get_implied_mtag_offset(expr, tag, &tmp_offset)) {
374efe51d0cSJohn Levon 				// FIXME:  look it up recursively?
375efe51d0cSJohn Levon 				if (tmp_offset)
376efe51d0cSJohn Levon 					return 0;
377efe51d0cSJohn Levon 				return 1;
378efe51d0cSJohn Levon 			}
3791f5207b7SJohn Levon 			return 0;
380efe51d0cSJohn Levon 		} else if (expr->type == EXPR_SYMBOL) {
381efe51d0cSJohn Levon 			return get_symbol_mtag(expr->symbol, tag);
382efe51d0cSJohn Levon 		}
383efe51d0cSJohn Levon 		return 0;
384efe51d0cSJohn Levon 	} else if (expr->type == EXPR_SYMBOL) {
385efe51d0cSJohn Levon 		return get_symbol_mtag(expr->symbol, tag);
3861f5207b7SJohn Levon 	}
387efe51d0cSJohn Levon 	return 0;
3881f5207b7SJohn Levon }
3891f5207b7SJohn Levon 
390efe51d0cSJohn Levon /*
391efe51d0cSJohn Levon  * This function takes an address and returns an sval.  Let's take some
392efe51d0cSJohn Levon  * example things you might pass to it:
393efe51d0cSJohn Levon  * foo->bar:
394efe51d0cSJohn Levon  *   If we were only called from smatch_math, we wouldn't need to bother with
395efe51d0cSJohn Levon  *   this because it's already been looked up in smatch_extra.c but this is
396efe51d0cSJohn Levon  *   also called from other places so we have to check smatch_extra.c.
397efe51d0cSJohn Levon  * &foo
398efe51d0cSJohn Levon  *   If "foo" is global return the mtag for "foo".
399efe51d0cSJohn Levon  * &foo.bar
400efe51d0cSJohn Levon  *   If "foo" is global return the mtag for "foo" + the offset of ".bar".
401efe51d0cSJohn Levon  * It also handles string literals.
402efe51d0cSJohn Levon  *
403efe51d0cSJohn Levon  */
get_mtag_sval(struct expression * expr,sval_t * sval)4041f5207b7SJohn Levon int get_mtag_sval(struct expression *expr, sval_t *sval)
4051f5207b7SJohn Levon {
4061f5207b7SJohn Levon 	struct symbol *type;
4071f5207b7SJohn Levon 	mtag_t tag;
4081f5207b7SJohn Levon 	int offset = 0;
4091f5207b7SJohn Levon 
4101f5207b7SJohn Levon 	if (bits_in_pointer != 64)
4111f5207b7SJohn Levon 		return 0;
4121f5207b7SJohn Levon 
4131f5207b7SJohn Levon 	expr = strip_expr(expr);
4141f5207b7SJohn Levon 
4151f5207b7SJohn Levon 	type = get_type(expr);
4161f5207b7SJohn Levon 	if (!type_is_ptr(type))
4171f5207b7SJohn Levon 		return 0;
4181f5207b7SJohn Levon 	/*
419efe51d0cSJohn Levon 	 * There are several options:
4201f5207b7SJohn Levon 	 *
421efe51d0cSJohn Levon 	 * If the expr is a string literal, that's an address/mtag.
422efe51d0cSJohn Levon 	 * SYM_ARRAY and SYM_FN are mtags.  There are "&foo" type addresses.
423efe51d0cSJohn Levon 	 * And there are saved pointers "p = &foo;"
4241f5207b7SJohn Levon 	 *
4251f5207b7SJohn Levon 	 */
4261f5207b7SJohn Levon 
4271f5207b7SJohn Levon 	if (expr->type == EXPR_STRING && get_string_mtag(expr, &tag))
4281f5207b7SJohn Levon 		goto found;
4291f5207b7SJohn Levon 
430efe51d0cSJohn Levon 	if (expr->type == EXPR_SYMBOL &&
431efe51d0cSJohn Levon 	    (type->type == SYM_ARRAY || type->type == SYM_FN) &&
432efe51d0cSJohn Levon 	    get_toplevel_mtag(expr->symbol, &tag))
4331f5207b7SJohn Levon 		goto found;
4341f5207b7SJohn Levon 
435efe51d0cSJohn Levon 	if (expr->type == EXPR_PREOP && expr->op == '&') {
436efe51d0cSJohn Levon 		expr = strip_expr(expr->unop);
437efe51d0cSJohn Levon 		if (expr_to_mtag_offset(expr, &tag, &offset))
438efe51d0cSJohn Levon 			goto found;
439efe51d0cSJohn Levon 		return 0;
440efe51d0cSJohn Levon 	}
441efe51d0cSJohn Levon 
4421f5207b7SJohn Levon 	if (get_implied_mtag_offset(expr, &tag, &offset))
4431f5207b7SJohn Levon 		goto found;
4441f5207b7SJohn Levon 
445efe51d0cSJohn Levon 	return 0;
446efe51d0cSJohn Levon found:
447efe51d0cSJohn Levon 	if (offset >= MTAG_OFFSET_MASK)
4481f5207b7SJohn Levon 		return 0;
4491f5207b7SJohn Levon 
4501f5207b7SJohn Levon 	sval->type = type;
4511f5207b7SJohn Levon 	sval->uvalue = tag | offset;
4521f5207b7SJohn Levon 
4531f5207b7SJohn Levon 	return 1;
4541f5207b7SJohn Levon }
4551f5207b7SJohn Levon 
register_mtag(int id)4561f5207b7SJohn Levon void register_mtag(int id)
4571f5207b7SJohn Levon {
4581f5207b7SJohn Levon 	my_id = id;
4591f5207b7SJohn Levon 
4601f5207b7SJohn Levon 
4611f5207b7SJohn Levon 	/*
4621f5207b7SJohn Levon 	 * The mtag stuff only works on 64 systems because we store the
4631f5207b7SJohn Levon 	 * information in the pointer itself.
4641f5207b7SJohn Levon 	 * bit 63   : set for alias mtags
4651f5207b7SJohn Levon 	 * bit 62-12: mtag hash
4661f5207b7SJohn Levon 	 * bit 11-0 : offset
4671f5207b7SJohn Levon 	 *
4681f5207b7SJohn Levon 	 */
4691f5207b7SJohn Levon 
4701f5207b7SJohn Levon 	add_hook(&global_variable, BASE_HOOK);
4711f5207b7SJohn Levon }
472