--- /dev/null	Fri Dec 21 15:00:26 2018
+++ new/usr/src/tools/smatch/src/smatch_fn_arg_link.c	Fri Dec 21 15:00:26 2018
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+/*
+ * What we're trying to do here is record links between function pointers and
+ * function data.  If you have foo->function(foo->data); that's very easy.  But
+ * the problem is maybe when you pass the function and the data as parameters.
+ *
+ */
+
+#include "smatch.h"
+#include <ctype.h>
+
+static int my_id;
+
+static void save_in_fn_ptr_data_link_table(struct expression *fn, struct expression *arg)
+{
+	struct symbol *fn_sym, *arg_sym;
+	struct symbol *type;
+	char *fn_name, *arg_name;
+	int sym_len;
+	char fn_buf[128];
+	char arg_buf[128];
+
+	fn_name = expr_to_var_sym(fn, &fn_sym);
+	arg_name = expr_to_var_sym(arg, &arg_sym);
+	if (!fn_sym || !fn_sym->ident || !arg_sym || !fn_name || !arg_name)
+		goto free;
+	if (fn_sym != arg_sym)
+		goto free;
+
+	sym_len = fn_sym->ident->len;
+
+	/* This is ignoring
+	 * net/mac80211/driver-ops.h:482 drv_sta_remove() FN: local->ops->sta_remove ARG: &local->hw
+	 * but ideally the restriction can be removed later.
+	 */
+	if (strncmp(fn_name, arg_name, sym_len) != 0)
+		goto free;
+
+	type = get_real_base_type(fn_sym);
+	if (!type)
+		goto free;
+	if (type->type != SYM_PTR)
+		goto free;
+	type = get_real_base_type(type);
+	if (!type || type->type != SYM_STRUCT || !type->ident)
+		goto free;
+
+	snprintf(fn_buf, sizeof(fn_buf), "(struct %s)%s", type->ident->name,
+		 fn_name + sym_len);
+
+	snprintf(arg_buf, sizeof(arg_buf), "(struct %s)%s", type->ident->name,
+		 arg_name + sym_len);
+
+	sql_insert_fn_ptr_data_link(fn_buf, arg_buf);
+free:
+	free_string(arg_name);
+	free_string(fn_name);
+}
+
+static int print_calls_parameter(struct expression *call)
+{
+	struct expression *arg;
+	int fn_param, arg_param;
+	char buf[32];
+
+	fn_param = get_param_num(call->fn);
+	if (fn_param < 0)
+		return 0;
+
+	arg = get_argument_from_call_expr(call->args, 0);
+	if (!arg)
+		return 0;
+
+	arg_param = get_param_num(arg);
+	if (arg_param < 0)
+		return 0;
+
+	snprintf(buf, sizeof(buf), "%d", arg_param);
+	sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
+	return 0;
+}
+
+static int print_call_is_linked(struct expression *call)
+{
+	struct expression *fn, *tmp;
+	struct expression *arg;
+	struct symbol *fn_sym;
+	struct symbol *arg_sym = NULL;
+	int i;
+
+	fn = strip_expr(call->fn);
+	tmp = get_assigned_expr(fn);
+	if (tmp)
+		fn = tmp;
+	if (fn->type != EXPR_DEREF || !fn->member)
+		return 0;
+
+	fn_sym = expr_to_sym(fn);
+	if (!fn_sym)
+		return 0;
+
+	i = -1;
+	FOR_EACH_PTR(call->args, arg) {
+		i++;
+		tmp = get_assigned_expr(arg);
+		if (tmp)
+			arg = tmp;
+		arg_sym = expr_to_sym(arg);
+		if (arg_sym == fn_sym) {
+			save_in_fn_ptr_data_link_table(fn, arg);
+			return 1;
+		}
+	} END_FOR_EACH_PTR(arg);
+
+	return 0;
+}
+
+static int is_recursive_call(struct expression *call)
+{
+	if (call->fn->type != EXPR_SYMBOL)
+		return 0;
+	if (call->fn->symbol == cur_func_sym)
+		return 1;
+	return 0;
+}
+
+static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value)
+{
+	struct expression *arg;
+	struct expression *tmp;
+	struct symbol *fn_sym, *arg_sym;
+	struct symbol *type;
+	int data_nr;
+	int fn_param, arg_param;
+
+	if (is_recursive_call(call))
+		return;
+
+	type = get_type(fn);
+	if (!type || type->type != SYM_PTR)
+		return;
+	type = get_real_base_type(type);
+	if (!type || type->type != SYM_FN)
+		return;
+	tmp = get_assigned_expr(fn);
+	if (tmp)
+		fn = tmp;
+
+	if (!isdigit(value[0]))
+		return;
+	data_nr = atoi(value);
+	arg = get_argument_from_call_expr(call->args, data_nr);
+	if (!arg)
+		return;
+	tmp = get_assigned_expr(arg);
+	if (tmp)
+		arg = tmp;
+
+	fn_param = get_param_num(fn);
+	arg_param = get_param_num(arg);
+	if (fn_param >= 0 && arg_param >= 0) {
+		char buf[32];
+
+		snprintf(buf, sizeof(buf), "%d", arg_param);
+		sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
+		return;
+	}
+
+	fn_sym = expr_to_sym(fn);
+	if (!fn_sym)
+		return;
+	arg_sym = expr_to_sym(arg);
+	if (arg_sym != fn_sym)
+		return;
+	save_in_fn_ptr_data_link_table(fn, tmp);
+}
+
+static void match_call_info(struct expression *call)
+{
+	if (print_calls_parameter(call))
+		return;
+	if (print_call_is_linked(call))
+		return;
+}
+
+void register_fn_arg_link(int id)
+{
+	my_id = id;
+
+	if (!option_info)
+		return;
+
+	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
+	select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data);
+}
+
