Netdev List
 help / color / mirror / Atom feed
* [net-next:master 375/376] net/core/rtnetlink.c:3099:1: warning: the frame size of 1280 bytes is larger than 1024 bytes
From: kbuild test robot @ 2018-06-01  5:07 UTC (permalink / raw)
  To: Kees Cook; +Cc: kbuild-all, netdev

[-- Attachment #1: Type: text/plain, Size: 18883 bytes --]

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git master
head:   4b8e6ac41a594ea67ded6af6af5935f03221ea4c
commit: ccf8dbcd062a930e64741c939ca784d15316aa0c [375/376] rtnetlink: Remove VLA usage
config: um-x86_64_defconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        git checkout ccf8dbcd062a930e64741c939ca784d15316aa0c
        # save the attached .config to linux build tree
        make ARCH=um SUBARCH=x86_64

All warnings (new ones prefixed by >>):

   net/core/rtnetlink.c: In function 'rtnl_newlink':
>> net/core/rtnetlink.c:3099:1: warning: the frame size of 1280 bytes is larger than 1024 bytes [-Wframe-larger-than=]
    }
    ^

vim +3099 net/core/rtnetlink.c

e7ed828f1 Vlad Dogaru       2011-01-13  2842  
c21ef3e34 David Ahern       2017-04-16  2843  static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
c21ef3e34 David Ahern       2017-04-16  2844  			struct netlink_ext_ack *extack)
38f7b870d Patrick McHardy   2007-06-13  2845  {
3b1e0a655 YOSHIFUJI Hideaki 2008-03-26  2846  	struct net *net = sock_net(skb->sk);
38f7b870d Patrick McHardy   2007-06-13  2847  	const struct rtnl_link_ops *ops;
ba7d49b1f Jiri Pirko        2014-01-22  2848  	const struct rtnl_link_ops *m_ops = NULL;
38f7b870d Patrick McHardy   2007-06-13  2849  	struct net_device *dev;
ba7d49b1f Jiri Pirko        2014-01-22  2850  	struct net_device *master_dev = NULL;
38f7b870d Patrick McHardy   2007-06-13  2851  	struct ifinfomsg *ifm;
38f7b870d Patrick McHardy   2007-06-13  2852  	char kind[MODULE_NAME_LEN];
38f7b870d Patrick McHardy   2007-06-13  2853  	char ifname[IFNAMSIZ];
38f7b870d Patrick McHardy   2007-06-13  2854  	struct nlattr *tb[IFLA_MAX+1];
38f7b870d Patrick McHardy   2007-06-13  2855  	struct nlattr *linkinfo[IFLA_INFO_MAX+1];
5517750f0 Tom Gundersen     2014-07-14  2856  	unsigned char name_assign_type = NET_NAME_USER;
38f7b870d Patrick McHardy   2007-06-13  2857  	int err;
38f7b870d Patrick McHardy   2007-06-13  2858  
95a5afca4 Johannes Berg     2008-10-16  2859  #ifdef CONFIG_MODULES
38f7b870d Patrick McHardy   2007-06-13  2860  replay:
8072f085d Thomas Graf       2007-07-31  2861  #endif
c21ef3e34 David Ahern       2017-04-16  2862  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack);
38f7b870d Patrick McHardy   2007-06-13  2863  	if (err < 0)
38f7b870d Patrick McHardy   2007-06-13  2864  		return err;
38f7b870d Patrick McHardy   2007-06-13  2865  
4ff66cae7 Christian Brauner 2018-02-07  2866  	err = rtnl_ensure_unique_netns(tb, extack, false);
4ff66cae7 Christian Brauner 2018-02-07  2867  	if (err < 0)
4ff66cae7 Christian Brauner 2018-02-07  2868  		return err;
4ff66cae7 Christian Brauner 2018-02-07  2869  
38f7b870d Patrick McHardy   2007-06-13  2870  	if (tb[IFLA_IFNAME])
38f7b870d Patrick McHardy   2007-06-13  2871  		nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
38f7b870d Patrick McHardy   2007-06-13  2872  	else
38f7b870d Patrick McHardy   2007-06-13  2873  		ifname[0] = '\0';
38f7b870d Patrick McHardy   2007-06-13  2874  
38f7b870d Patrick McHardy   2007-06-13  2875  	ifm = nlmsg_data(nlh);
38f7b870d Patrick McHardy   2007-06-13  2876  	if (ifm->ifi_index > 0)
881d966b4 Eric W. Biederman 2007-09-17  2877  		dev = __dev_get_by_index(net, ifm->ifi_index);
e7ed828f1 Vlad Dogaru       2011-01-13  2878  	else {
e7ed828f1 Vlad Dogaru       2011-01-13  2879  		if (ifname[0])
881d966b4 Eric W. Biederman 2007-09-17  2880  			dev = __dev_get_by_name(net, ifname);
38f7b870d Patrick McHardy   2007-06-13  2881  		else
38f7b870d Patrick McHardy   2007-06-13  2882  			dev = NULL;
e7ed828f1 Vlad Dogaru       2011-01-13  2883  	}
38f7b870d Patrick McHardy   2007-06-13  2884  
ba7d49b1f Jiri Pirko        2014-01-22  2885  	if (dev) {
ba7d49b1f Jiri Pirko        2014-01-22  2886  		master_dev = netdev_master_upper_dev_get(dev);
ba7d49b1f Jiri Pirko        2014-01-22  2887  		if (master_dev)
ba7d49b1f Jiri Pirko        2014-01-22  2888  			m_ops = master_dev->rtnl_link_ops;
ba7d49b1f Jiri Pirko        2014-01-22  2889  	}
ba7d49b1f Jiri Pirko        2014-01-22  2890  
e0d087af7 Eric Dumazet      2009-11-07  2891  	err = validate_linkmsg(dev, tb);
e0d087af7 Eric Dumazet      2009-11-07  2892  	if (err < 0)
1840bb13c Thomas Graf       2008-02-23  2893  		return err;
1840bb13c Thomas Graf       2008-02-23  2894  
38f7b870d Patrick McHardy   2007-06-13  2895  	if (tb[IFLA_LINKINFO]) {
38f7b870d Patrick McHardy   2007-06-13  2896  		err = nla_parse_nested(linkinfo, IFLA_INFO_MAX,
fceb6435e Johannes Berg     2017-04-12  2897  				       tb[IFLA_LINKINFO], ifla_info_policy,
fceb6435e Johannes Berg     2017-04-12  2898  				       NULL);
38f7b870d Patrick McHardy   2007-06-13  2899  		if (err < 0)
38f7b870d Patrick McHardy   2007-06-13  2900  			return err;
38f7b870d Patrick McHardy   2007-06-13  2901  	} else
38f7b870d Patrick McHardy   2007-06-13  2902  		memset(linkinfo, 0, sizeof(linkinfo));
38f7b870d Patrick McHardy   2007-06-13  2903  
38f7b870d Patrick McHardy   2007-06-13  2904  	if (linkinfo[IFLA_INFO_KIND]) {
38f7b870d Patrick McHardy   2007-06-13  2905  		nla_strlcpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
38f7b870d Patrick McHardy   2007-06-13  2906  		ops = rtnl_link_ops_get(kind);
38f7b870d Patrick McHardy   2007-06-13  2907  	} else {
38f7b870d Patrick McHardy   2007-06-13  2908  		kind[0] = '\0';
38f7b870d Patrick McHardy   2007-06-13  2909  		ops = NULL;
38f7b870d Patrick McHardy   2007-06-13  2910  	}
38f7b870d Patrick McHardy   2007-06-13  2911  
38f7b870d Patrick McHardy   2007-06-13  2912  	if (1) {
ccf8dbcd0 Kees Cook         2018-05-30  2913  		struct nlattr *attr[RTNL_MAX_TYPE + 1];
ccf8dbcd0 Kees Cook         2018-05-30  2914  		struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
ba7d49b1f Jiri Pirko        2014-01-22  2915  		struct nlattr **data = NULL;
ba7d49b1f Jiri Pirko        2014-01-22  2916  		struct nlattr **slave_data = NULL;
317f4810e Nicolas Dichtel   2015-01-15  2917  		struct net *dest_net, *link_net = NULL;
38f7b870d Patrick McHardy   2007-06-13  2918  
38f7b870d Patrick McHardy   2007-06-13  2919  		if (ops) {
ccf8dbcd0 Kees Cook         2018-05-30  2920  			if (ops->maxtype > RTNL_MAX_TYPE)
ccf8dbcd0 Kees Cook         2018-05-30  2921  				return -EINVAL;
ccf8dbcd0 Kees Cook         2018-05-30  2922  
38f7b870d Patrick McHardy   2007-06-13  2923  			if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
38f7b870d Patrick McHardy   2007-06-13  2924  				err = nla_parse_nested(attr, ops->maxtype,
38f7b870d Patrick McHardy   2007-06-13  2925  						       linkinfo[IFLA_INFO_DATA],
fceb6435e Johannes Berg     2017-04-12  2926  						       ops->policy, NULL);
38f7b870d Patrick McHardy   2007-06-13  2927  				if (err < 0)
38f7b870d Patrick McHardy   2007-06-13  2928  					return err;
38f7b870d Patrick McHardy   2007-06-13  2929  				data = attr;
38f7b870d Patrick McHardy   2007-06-13  2930  			}
38f7b870d Patrick McHardy   2007-06-13  2931  			if (ops->validate) {
a8b8a889e Matthias Schiffer 2017-06-25  2932  				err = ops->validate(tb, data, extack);
38f7b870d Patrick McHardy   2007-06-13  2933  				if (err < 0)
38f7b870d Patrick McHardy   2007-06-13  2934  					return err;
38f7b870d Patrick McHardy   2007-06-13  2935  			}
38f7b870d Patrick McHardy   2007-06-13  2936  		}
38f7b870d Patrick McHardy   2007-06-13  2937  
ba7d49b1f Jiri Pirko        2014-01-22  2938  		if (m_ops) {
ccf8dbcd0 Kees Cook         2018-05-30  2939  			if (ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
ccf8dbcd0 Kees Cook         2018-05-30  2940  				return -EINVAL;
ccf8dbcd0 Kees Cook         2018-05-30  2941  
ba7d49b1f Jiri Pirko        2014-01-22  2942  			if (m_ops->slave_maxtype &&
ba7d49b1f Jiri Pirko        2014-01-22  2943  			    linkinfo[IFLA_INFO_SLAVE_DATA]) {
ba7d49b1f Jiri Pirko        2014-01-22  2944  				err = nla_parse_nested(slave_attr,
ba7d49b1f Jiri Pirko        2014-01-22  2945  						       m_ops->slave_maxtype,
ba7d49b1f Jiri Pirko        2014-01-22  2946  						       linkinfo[IFLA_INFO_SLAVE_DATA],
fceb6435e Johannes Berg     2017-04-12  2947  						       m_ops->slave_policy,
fceb6435e Johannes Berg     2017-04-12  2948  						       NULL);
ba7d49b1f Jiri Pirko        2014-01-22  2949  				if (err < 0)
ba7d49b1f Jiri Pirko        2014-01-22  2950  					return err;
ba7d49b1f Jiri Pirko        2014-01-22  2951  				slave_data = slave_attr;
ba7d49b1f Jiri Pirko        2014-01-22  2952  			}
ba7d49b1f Jiri Pirko        2014-01-22  2953  		}
ba7d49b1f Jiri Pirko        2014-01-22  2954  
38f7b870d Patrick McHardy   2007-06-13  2955  		if (dev) {
90c325e3b Nicolas Dichtel   2014-09-01  2956  			int status = 0;
38f7b870d Patrick McHardy   2007-06-13  2957  
38f7b870d Patrick McHardy   2007-06-13  2958  			if (nlh->nlmsg_flags & NLM_F_EXCL)
38f7b870d Patrick McHardy   2007-06-13  2959  				return -EEXIST;
38f7b870d Patrick McHardy   2007-06-13  2960  			if (nlh->nlmsg_flags & NLM_F_REPLACE)
38f7b870d Patrick McHardy   2007-06-13  2961  				return -EOPNOTSUPP;
38f7b870d Patrick McHardy   2007-06-13  2962  
38f7b870d Patrick McHardy   2007-06-13  2963  			if (linkinfo[IFLA_INFO_DATA]) {
38f7b870d Patrick McHardy   2007-06-13  2964  				if (!ops || ops != dev->rtnl_link_ops ||
38f7b870d Patrick McHardy   2007-06-13  2965  				    !ops->changelink)
38f7b870d Patrick McHardy   2007-06-13  2966  					return -EOPNOTSUPP;
38f7b870d Patrick McHardy   2007-06-13  2967  
ad744b223 Matthias Schiffer 2017-06-25  2968  				err = ops->changelink(dev, tb, data, extack);
38f7b870d Patrick McHardy   2007-06-13  2969  				if (err < 0)
38f7b870d Patrick McHardy   2007-06-13  2970  					return err;
ba9989069 Nicolas Dichtel   2014-09-01  2971  				status |= DO_SETLINK_NOTIFY;
38f7b870d Patrick McHardy   2007-06-13  2972  			}
38f7b870d Patrick McHardy   2007-06-13  2973  
ba7d49b1f Jiri Pirko        2014-01-22  2974  			if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
ba7d49b1f Jiri Pirko        2014-01-22  2975  				if (!m_ops || !m_ops->slave_changelink)
ba7d49b1f Jiri Pirko        2014-01-22  2976  					return -EOPNOTSUPP;
ba7d49b1f Jiri Pirko        2014-01-22  2977  
ba7d49b1f Jiri Pirko        2014-01-22  2978  				err = m_ops->slave_changelink(master_dev, dev,
17dd0ec47 Matthias Schiffer 2017-06-25  2979  							      tb, slave_data,
17dd0ec47 Matthias Schiffer 2017-06-25  2980  							      extack);
ba7d49b1f Jiri Pirko        2014-01-22  2981  				if (err < 0)
ba7d49b1f Jiri Pirko        2014-01-22  2982  					return err;
ba9989069 Nicolas Dichtel   2014-09-01  2983  				status |= DO_SETLINK_NOTIFY;
ba7d49b1f Jiri Pirko        2014-01-22  2984  			}
ba7d49b1f Jiri Pirko        2014-01-22  2985  
ddf9f9707 Jakub Kicinski    2017-04-30  2986  			return do_setlink(skb, dev, ifm, extack, tb, ifname,
ddf9f9707 Jakub Kicinski    2017-04-30  2987  					  status);
38f7b870d Patrick McHardy   2007-06-13  2988  		}
38f7b870d Patrick McHardy   2007-06-13  2989  
ffa934f19 Patrick McHardy   2011-01-20  2990  		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
ffa934f19 Patrick McHardy   2011-01-20  2991  			if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
90f62cf30 Eric W. Biederman 2014-04-23  2992  				return rtnl_group_changelink(skb, net,
ffa934f19 Patrick McHardy   2011-01-20  2993  						nla_get_u32(tb[IFLA_GROUP]),
ddf9f9707 Jakub Kicinski    2017-04-30  2994  						ifm, extack, tb);
38f7b870d Patrick McHardy   2007-06-13  2995  			return -ENODEV;
ffa934f19 Patrick McHardy   2011-01-20  2996  		}
38f7b870d Patrick McHardy   2007-06-13  2997  
160ca0142 Theuns Verwoerd   2017-01-31  2998  		if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
38f7b870d Patrick McHardy   2007-06-13  2999  			return -EOPNOTSUPP;
38f7b870d Patrick McHardy   2007-06-13  3000  
38f7b870d Patrick McHardy   2007-06-13  3001  		if (!ops) {
95a5afca4 Johannes Berg     2008-10-16  3002  #ifdef CONFIG_MODULES
38f7b870d Patrick McHardy   2007-06-13  3003  			if (kind[0]) {
38f7b870d Patrick McHardy   2007-06-13  3004  				__rtnl_unlock();
38f7b870d Patrick McHardy   2007-06-13  3005  				request_module("rtnl-link-%s", kind);
38f7b870d Patrick McHardy   2007-06-13  3006  				rtnl_lock();
38f7b870d Patrick McHardy   2007-06-13  3007  				ops = rtnl_link_ops_get(kind);
38f7b870d Patrick McHardy   2007-06-13  3008  				if (ops)
38f7b870d Patrick McHardy   2007-06-13  3009  					goto replay;
38f7b870d Patrick McHardy   2007-06-13  3010  			}
38f7b870d Patrick McHardy   2007-06-13  3011  #endif
38f7b870d Patrick McHardy   2007-06-13  3012  			return -EOPNOTSUPP;
38f7b870d Patrick McHardy   2007-06-13  3013  		}
38f7b870d Patrick McHardy   2007-06-13  3014  
b0ab2fabb Jiri Pirko        2014-06-26  3015  		if (!ops->setup)
b0ab2fabb Jiri Pirko        2014-06-26  3016  			return -EOPNOTSUPP;
b0ab2fabb Jiri Pirko        2014-06-26  3017  
5517750f0 Tom Gundersen     2014-07-14  3018  		if (!ifname[0]) {
38f7b870d Patrick McHardy   2007-06-13  3019  			snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
5517750f0 Tom Gundersen     2014-07-14  3020  			name_assign_type = NET_NAME_ENUM;
5517750f0 Tom Gundersen     2014-07-14  3021  		}
38f7b870d Patrick McHardy   2007-06-13  3022  
5bb8ed075 Christian Brauner 2018-01-29  3023  		dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
13ad17745 Eric W. Biederman 2011-01-29  3024  		if (IS_ERR(dest_net))
13ad17745 Eric W. Biederman 2011-01-29  3025  			return PTR_ERR(dest_net);
13ad17745 Eric W. Biederman 2011-01-29  3026  
317f4810e Nicolas Dichtel   2015-01-15  3027  		if (tb[IFLA_LINK_NETNSID]) {
317f4810e Nicolas Dichtel   2015-01-15  3028  			int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
317f4810e Nicolas Dichtel   2015-01-15  3029  
317f4810e Nicolas Dichtel   2015-01-15  3030  			link_net = get_net_ns_by_id(dest_net, id);
317f4810e Nicolas Dichtel   2015-01-15  3031  			if (!link_net) {
317f4810e Nicolas Dichtel   2015-01-15  3032  				err =  -EINVAL;
317f4810e Nicolas Dichtel   2015-01-15  3033  				goto out;
317f4810e Nicolas Dichtel   2015-01-15  3034  			}
06615bed6 Eric W. Biederman 2015-02-26  3035  			err = -EPERM;
06615bed6 Eric W. Biederman 2015-02-26  3036  			if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
06615bed6 Eric W. Biederman 2015-02-26  3037  				goto out;
317f4810e Nicolas Dichtel   2015-01-15  3038  		}
317f4810e Nicolas Dichtel   2015-01-15  3039  
317f4810e Nicolas Dichtel   2015-01-15  3040  		dev = rtnl_create_link(link_net ? : dest_net, ifname,
317f4810e Nicolas Dichtel   2015-01-15  3041  				       name_assign_type, ops, tb);
9c7dafbfa Pavel Emelyanov   2012-08-08  3042  		if (IS_ERR(dev)) {
e71992889 Pavel Emelianov   2007-08-08  3043  			err = PTR_ERR(dev);
9c7dafbfa Pavel Emelyanov   2012-08-08  3044  			goto out;
9c7dafbfa Pavel Emelyanov   2012-08-08  3045  		}
9c7dafbfa Pavel Emelyanov   2012-08-08  3046  
9c7dafbfa Pavel Emelyanov   2012-08-08  3047  		dev->ifindex = ifm->ifi_index;
9c7dafbfa Pavel Emelyanov   2012-08-08  3048  
0e0eee246 Cong Wang         2014-02-11  3049  		if (ops->newlink) {
7a3f4a185 Matthias Schiffer 2017-06-25  3050  			err = ops->newlink(link_net ? : net, dev, tb, data,
7a3f4a185 Matthias Schiffer 2017-06-25  3051  					   extack);
0e0eee246 Cong Wang         2014-02-11  3052  			/* Drivers should call free_netdev() in ->destructor
e51fb1523 Cong Wang         2014-06-03  3053  			 * and unregister it on failure after registration
e51fb1523 Cong Wang         2014-06-03  3054  			 * so that device could be finally freed in rtnl_unlock.
0e0eee246 Cong Wang         2014-02-11  3055  			 */
e51fb1523 Cong Wang         2014-06-03  3056  			if (err < 0) {
e51fb1523 Cong Wang         2014-06-03  3057  				/* If device is not registered at all, free it now */
e51fb1523 Cong Wang         2014-06-03  3058  				if (dev->reg_state == NETREG_UNINITIALIZED)
e51fb1523 Cong Wang         2014-06-03  3059  					free_netdev(dev);
0e0eee246 Cong Wang         2014-02-11  3060  				goto out;
e51fb1523 Cong Wang         2014-06-03  3061  			}
0e0eee246 Cong Wang         2014-02-11  3062  		} else {
2d85cba2b Patrick McHardy   2007-07-11  3063  			err = register_netdevice(dev);
fce9b9be8 Dan Carpenter     2013-08-14  3064  			if (err < 0) {
38f7b870d Patrick McHardy   2007-06-13  3065  				free_netdev(dev);
3729d5021 Patrick McHardy   2010-02-26  3066  				goto out;
fce9b9be8 Dan Carpenter     2013-08-14  3067  			}
0e0eee246 Cong Wang         2014-02-11  3068  		}
3729d5021 Patrick McHardy   2010-02-26  3069  		err = rtnl_configure_link(dev, ifm);
436389007 David S. Miller   2015-03-10  3070  		if (err < 0)
436389007 David S. Miller   2015-03-10  3071  			goto out_unregister;
bdef279b9 Nicolas Dichtel   2015-01-20  3072  		if (link_net) {
317f4810e Nicolas Dichtel   2015-01-15  3073  			err = dev_change_net_namespace(dev, dest_net, ifname);
bdef279b9 Nicolas Dichtel   2015-01-20  3074  			if (err < 0)
436389007 David S. Miller   2015-03-10  3075  				goto out_unregister;
bdef279b9 Nicolas Dichtel   2015-01-20  3076  		}
160ca0142 Theuns Verwoerd   2017-01-31  3077  		if (tb[IFLA_MASTER]) {
33eaf2a6e David Ahern       2017-10-04  3078  			err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]),
33eaf2a6e David Ahern       2017-10-04  3079  					    extack);
160ca0142 Theuns Verwoerd   2017-01-31  3080  			if (err)
160ca0142 Theuns Verwoerd   2017-01-31  3081  				goto out_unregister;
160ca0142 Theuns Verwoerd   2017-01-31  3082  		}
3729d5021 Patrick McHardy   2010-02-26  3083  out:
317f4810e Nicolas Dichtel   2015-01-15  3084  		if (link_net)
317f4810e Nicolas Dichtel   2015-01-15  3085  			put_net(link_net);
81adee47d Eric W. Biederman 2009-11-08  3086  		put_net(dest_net);
38f7b870d Patrick McHardy   2007-06-13  3087  		return err;
436389007 David S. Miller   2015-03-10  3088  out_unregister:
436389007 David S. Miller   2015-03-10  3089  		if (ops->newlink) {
436389007 David S. Miller   2015-03-10  3090  			LIST_HEAD(list_kill);
436389007 David S. Miller   2015-03-10  3091  
436389007 David S. Miller   2015-03-10  3092  			ops->dellink(dev, &list_kill);
436389007 David S. Miller   2015-03-10  3093  			unregister_netdevice_many(&list_kill);
436389007 David S. Miller   2015-03-10  3094  		} else {
436389007 David S. Miller   2015-03-10  3095  			unregister_netdevice(dev);
436389007 David S. Miller   2015-03-10  3096  		}
436389007 David S. Miller   2015-03-10  3097  		goto out;
38f7b870d Patrick McHardy   2007-06-13  3098  	}
38f7b870d Patrick McHardy   2007-06-13 @3099  }
38f7b870d Patrick McHardy   2007-06-13  3100  

:::::: The code at line 3099 was first introduced by commit
:::::: 38f7b870d4a6a5d3ec21557e849620cb7d032965 [RTNETLINK]: Link creation API

:::::: TO: Patrick McHardy <kaber@trash.net>
:::::: CC: David S. Miller <davem@sunset.davemloft.net>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 7394 bytes --]

^ permalink raw reply

* Re: [PATCH net-next] net: phy: consider PHY_IGNORE_INTERRUPT in state machine PHY_NOLINK handling
From: Heiner Kallweit @ 2018-06-01  6:13 UTC (permalink / raw)
  To: David Miller; +Cc: f.fainelli, andrew, netdev
In-Reply-To: <20180531.212616.129115220944192049.davem@davemloft.net>

On 01.06.2018 03:26, David Miller wrote:
> From: Heiner Kallweit <hkallweit1@gmail.com>
> Date: Wed, 30 May 2018 22:13:20 +0200
> 
>> We can bail out immediately also in case of PHY_IGNORE_INTERRUPT because
>> phy_mac_interupt() informs us once the link is up.
>>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> 
> When state is PHY_NOLINK, the phy_mac_interrupt() code paths
> will change the state to PHY_CHANGELINK before queueing up
> the state machine invocation.
> 
> So I can't even see how we can enter phy_state_machine with
> ->state == PHY_NOLINK is the mac interrupt paths are being
> used properly.
> 
We could enter the state machine with PHY_NOLINK in case
any other activity triggers a state machine run whilst
the link is down. But I'm not sure whether such a
scenario exists.

> Therefore it looks like the code as written is harmless.
> 
> Did you actually hit a problem with this test or is this
> a change based purely upon code inspection?
> 
Right, there is no actual problem, the existing code is
harmless and the change is just based on code inspection.
Small benefit is that it makes clearer that this code path
is applicable in polling mode only.

^ permalink raw reply

* Re: [PATCH net-next] net/smc: fix error return code in smc_setsockopt()
From: Ursula Braun @ 2018-06-01  7:02 UTC (permalink / raw)
  To: Wei Yongjun; +Cc: linux-s390, netdev, kernel-janitors
In-Reply-To: <1527733882-149144-1-git-send-email-weiyongjun1@huawei.com>



On 05/31/2018 04:31 AM, Wei Yongjun wrote:
> Fix to return error code -EINVAL instead of 0 if optlen is invalid.
> 
> Fixes: 01d2f7e2cdd3 ("net/smc: sockopts TCP_NODELAY and TCP_CORK")
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
> ---
>  net/smc/af_smc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
> index 2c369d4..973b447 100644
> --- a/net/smc/af_smc.c
> +++ b/net/smc/af_smc.c
> @@ -1420,7 +1420,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
>  		return rc;
>  
>  	if (optlen < sizeof(int))
> -		return rc;
> +		return -EINVAL;
>  	get_user(val, (int __user *)optval);
>  
>  	lock_sock(sk);
> 

Thanks for reporting this error. Your fix is fine, but I think we can get rid of
this check at all, since it is already checked in the tcp-code invoked before with

   smc->clcsock->ops->setsockopt(smc->clcsock, level, optname, optval, optlen)

^ permalink raw reply

* Re: [PATCH V4] mlx4_core: allocate ICM memory in page size chunks
From: Michal Hocko @ 2018-06-01  7:31 UTC (permalink / raw)
  To: Qing Huang
  Cc: Eric Dumazet, David Miller, tariqt, haakon.bugge, yanjun.zhu,
	netdev, linux-rdma, linux-kernel, gi-oh.kim,
	santosh.shilimkar@oracle.com
In-Reply-To: <7d8f52e1-aa16-d20c-a9a8-35ad88c0b1ab@oracle.com>

On Thu 31-05-18 19:04:46, Qing Huang wrote:
> 
> 
> On 5/31/2018 2:10 AM, Michal Hocko wrote:
> > On Thu 31-05-18 10:55:32, Michal Hocko wrote:
> > > On Thu 31-05-18 04:35:31, Eric Dumazet wrote:
> > [...]
> > > > I merely copied/pasted from alloc_skb_with_frags() :/
> > > I will have a look at it. Thanks!
> > OK, so this is an example of an incremental development ;).
> > 
> > __GFP_NORETRY was added by ed98df3361f0 ("net: use __GFP_NORETRY for
> > high order allocations") to prevent from OOM killer. Yet this was
> > not enough because fb05e7a89f50 ("net: don't wait for order-3 page
> > allocation") didn't want an excessive reclaim for non-costly orders
> > so it made it completely NOWAIT while it preserved __GFP_NORETRY in
> > place which is now redundant. Should I send a patch?
> > 
> 
> Just curious, how about GFP_ATOMIC flag? Would it work in a similar fashion?
> We experimented
> with it a bit in the past but it seemed to cause other issue in our tests.
> :-)

GFP_ATOMIC is a non-sleeping (aka no reclaim) context with an access to
memory reserves. So the risk is that you deplete those reserves and
cause issues to other subsystems which need them as well.

> By the way, we didn't encounter any OOM killer events. It seemed that the
> mlx4_alloc_icm() triggered slowpath.
> We still had about 2GB free memory while it was highly fragmented.

The compaction was able to make a reasonable forward progress for you.
But considering mlx4_alloc_icm is called with GFP_KERNEL resp. GFP_HIGHUSER
then the OOM killer is clearly possible as long as the order is lower
than 4.

>  #0 [ffff8801f308b380] remove_migration_pte at ffffffff811f0e0b
>  #1 [ffff8801f308b3e0] rmap_walk_file at ffffffff811cb890
>  #2 [ffff8801f308b440] rmap_walk at ffffffff811cbaf2
>  #3 [ffff8801f308b450] remove_migration_ptes at ffffffff811f0db0
>  #4 [ffff8801f308b490] __unmap_and_move at ffffffff811f2ea6
>  #5 [ffff8801f308b4e0] unmap_and_move at ffffffff811f2fc5
>  #6 [ffff8801f308b540] migrate_pages at ffffffff811f3219
>  #7 [ffff8801f308b5c0] compact_zone at ffffffff811b707e
>  #8 [ffff8801f308b650] compact_zone_order at ffffffff811b735d
>  #9 [ffff8801f308b6e0] try_to_compact_pages at ffffffff811b7485
> #10 [ffff8801f308b770] __alloc_pages_direct_compact at ffffffff81195f96
> #11 [ffff8801f308b7b0] __alloc_pages_slowpath at ffffffff811978a1
> #12 [ffff8801f308b890] __alloc_pages_nodemask at ffffffff81197ec1
> #13 [ffff8801f308b970] alloc_pages_current at ffffffff811e261f
> #14 [ffff8801f308b9e0] mlx4_alloc_icm at ffffffffa01f39b2 [mlx4_core]
> 
> Thanks!

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply

* Re: [PATCH] netfilter: nft_numgen: fix ptr_ret.cocci warnings
From: Pablo Neira Ayuso @ 2018-06-01  7:46 UTC (permalink / raw)
  To: Laura Garcia
  Cc: kbuild test robot, kbuild-all, Jozsef Kadlecsik, Florian Westphal,
	Netfilter Development Mailing list, coreteam, netdev,
	linux-kernel
In-Reply-To: <CAF90-WhsJTVcM+N_7n+4Zf_Knqzxc0UPv4i7bAM6Mvqu-GLHCw@mail.gmail.com>

On Thu, May 24, 2018 at 12:40:04PM +0200, Laura Garcia wrote:
> On Wed, May 23, 2018 at 12:58 PM, kbuild test robot
> <fengguang.wu@intel.com> wrote:
> > From: kbuild test robot <fengguang.wu@intel.com>
> >
> > net/netfilter/nft_numgen.c:117:1-3: WARNING: PTR_ERR_OR_ZERO can be used
> >
> >
> >  Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR
> >
> > Generated by: scripts/coccinelle/api/ptr_ret.cocci
> >
> > Fixes: d734a2888922 ("netfilter: nft_numgen: add map lookups for numgen statements")
> > CC: Laura Garcia Liebana <nevola@gmail.com>
> > Signed-off-by: kbuild test robot <fengguang.wu@intel.com>
> 
> Acked-by: Laura Garcia Liebana <nevola@gmail.com>

Applied, thanks.

^ permalink raw reply

* Re: [PATCH v2] netfilter: nfnetlink: Remove VLA usage
From: Pablo Neira Ayuso @ 2018-06-01  7:48 UTC (permalink / raw)
  To: Kees Cook
  Cc: Jozsef Kadlecsik, Florian Westphal, David S. Miller,
	netfilter-devel, coreteam, netdev, linux-kernel
In-Reply-To: <20180530191756.GA39504@beast>

On Wed, May 30, 2018 at 12:17:56PM -0700, Kees Cook wrote:
> In the quest to remove all stack VLA usage from the kernel[1], this
> allocates the maximum size expected for all possible attrs and adds
> sanity-checks at both registration and usage to make sure nothing
> gets out of sync.
> 
> [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com

Applied, thanks Kees.

^ permalink raw reply

* Re: [PATCH bpf-next] bpf: prevent non-IPv4 socket to be added into sock hash
From: John Fastabend @ 2018-06-01  7:56 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Wei Wang, netdev, Willem de Bruijn
In-Reply-To: <CANn89iLocj4Gth3R0+a4ppRhYHV=buWSgfEYLg+zJay25D477g@mail.gmail.com>

On 05/31/2018 06:00 PM, Eric Dumazet wrote:
> On Thu, May 31, 2018 at 7:32 PM John Fastabend <john.fastabend@gmail.com> wrote:
>>
>>
>> Hi Wei,
>>
>> Thanks for the report and fix. It would be better to fix the
>> root cause so that IPv6 works as intended.
>>
>> I'm testing the following now,
>>
>> Author: John Fastabend <john.fastabend@gmail.com>
>> Date:   Thu May 31 14:38:59 2018 -0700
>>
>>     sockmap: fix crash when ipv6 sock is added by adding support for IPv6
>>
>>     Apparently we had a testing escape and missed IPv6. This fixes a crash
>>     where we assign tcp_prot to IPv6 sockets instead of tcpv6_prot.
>>
>>     Signed-off-by: John Fastabend <john.fastabend@gmail.com>
>>
> 
> Hi John
> 
> In any case, please forward correct attribution for Wei's work, and
> syzbot 'Reported-by'

Will send update with tags in a moment.

> 
> Are you sure you are handling IPv4 mapped in IPv6 sockets as well ?
> 

No, will look into it. Although I didn't see any code to handle it
in the ./net/tls case either so if there is some issue with this it
could possibly exist in both ULPs. I guess if ipv4 mapped ipv6
changes prot or callbacks then we could stomp on it.

> Thanks.
> 

^ permalink raw reply

* Re: [PATCH net-next] netfilter: nf_tables: check msg_type before nft_trans_set(trans)
From: Pablo Neira Ayuso @ 2018-06-01  8:14 UTC (permalink / raw)
  To: Florian Westphal
  Cc: Alexey Kodanev, netfilter-devel, Jozsef Kadlecsik, coreteam,
	netdev
In-Reply-To: <20180531190731.rrwayqgnnnd2fasn@breakpoint.cc>

On Thu, May 31, 2018 at 09:07:31PM +0200, Florian Westphal wrote:
> Alexey Kodanev <alexey.kodanev@oracle.com> wrote:
> > The patch moves the "trans->msg_type == NFT_MSG_NEWSET" check before
> > using nft_trans_set(trans). Otherwise we can get out of bounds read.
> 
> Indeed, thanks for fixining this.
> 
> Acked-by: Florian Westphal <fw@strlen.de>

Also applied to nf.git, thanks!

^ permalink raw reply

* [PATCH net-next] rtnetlink: Fix null-ptr-deref in rtnl_newlink
From: Prashant Bhole @ 2018-06-01  8:16 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet
  Cc: Prashant Bhole, Daniel Borkmann, Alexei Starovoitov, Kirill Tkhai,
	Florian Westphal, netdev

In rtnl_newlink(), NULL check is performed on m_ops however member of
ops is accessed. Fixed by accessing member of m_ops instead of ops.

[  345.432629] BUG: KASAN: null-ptr-deref in rtnl_newlink+0x400/0x1110
[  345.432629] Read of size 4 at addr 0000000000000088 by task ip/986
[  345.432629]
[  345.432629] CPU: 1 PID: 986 Comm: ip Not tainted 4.17.0-rc6+ #9
[  345.432629] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[  345.432629] Call Trace:
[  345.432629]  dump_stack+0xc6/0x150
[  345.432629]  ? dump_stack_print_info.cold.0+0x1b/0x1b
[  345.432629]  ? kasan_report+0xb4/0x410
[  345.432629]  kasan_report.cold.4+0x8f/0x91
[  345.432629]  ? rtnl_newlink+0x400/0x1110
[  345.432629]  rtnl_newlink+0x400/0x1110
[...]

Fixes: ccf8dbcd062a ("rtnetlink: Remove VLA usage")
Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
---
 net/core/rtnetlink.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 8ca49a0e13fb..9a1ba2015ad8 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2936,7 +2936,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 		}
 
 		if (m_ops) {
-			if (ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
+			if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
 				return -EINVAL;
 
 			if (m_ops->slave_maxtype &&
-- 
2.17.0

^ permalink raw reply related

* [PATCH iproute2] devlink: don't enforce NETLINK_{CAP,EXT}_ACK sock opts
From: Ivan Vecera @ 2018-06-01  8:18 UTC (permalink / raw)
  To: netdev; +Cc: Arkadi Sharshevsky, Stephen Hemminger

Since commit 049c58539f5d ("devlink: mnlg: Add support for extended ack")
devlink requires NETLINK_{CAP,EXT}_ACK. This prevents devlink from
working with older kernels that don't support these features.

host # ./devlink/devlink
Failed to connect to devlink Netlink

Fixes: 049c58539f5d ("devlink: mnlg: Add support for extended ack")
Cc: Arkadi Sharshevsky <arkadis@mellanox.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 devlink/mnlg.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/devlink/mnlg.c b/devlink/mnlg.c
index 3d28453a..c33c90be 100644
--- a/devlink/mnlg.c
+++ b/devlink/mnlg.c
@@ -271,15 +271,9 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
 	if (!nlg->nl)
 		goto err_mnl_socket_open;
 
-	err = mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one,
-				    sizeof(one));
-	if (err)
-		goto err_mnl_set_ack;
-
-	err = mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one,
-				    sizeof(one));
-	if (err)
-		goto err_mnl_set_ext_ack;
+	/* Older kernels may no support capped/extended ACK reporting */
+	mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one));
+	mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one));
 
 	err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID);
 	if (err < 0)
@@ -305,8 +299,6 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
 err_mnlg_socket_recv_run:
 err_mnlg_socket_send:
 err_mnl_socket_bind:
-err_mnl_set_ext_ack:
-err_mnl_set_ack:
 	mnl_socket_close(nlg->nl);
 err_mnl_socket_open:
 	free(nlg->buf);
-- 
2.16.1

^ permalink raw reply related

* [bpf PATCH] bpf: sockmap, fix crash when ipv6 sock is added
From: John Fastabend @ 2018-06-01  8:21 UTC (permalink / raw)
  To: edumazet, ast, daniel; +Cc: netdev

This fixes a crash where we assign tcp_prot to IPv6 sockets instead
of tcpv6_prot.

Previously we overwrote the sk->prot field with tcp_prot even in the
AF_INET6 case. This patch ensures the correct tcp_prot and tcpv6_prot
are used.

Fixes: 174a79ff9515 ("bpf: sockmap with sk redirect support")
Reported-by: syzbot+5c063698bdbfac19f363@syzkaller.appspotmail.com
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Wei Wang <weiwan@google.com>
---
 kernel/bpf/sockmap.c |   15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c
index 95a84b2..d16b5a3 100644
--- a/kernel/bpf/sockmap.c
+++ b/kernel/bpf/sockmap.c
@@ -41,6 +41,7 @@
 #include <linux/mm.h>
 #include <net/strparser.h>
 #include <net/tcp.h>
+#include <net/transp_v6.h>
 #include <linux/ptr_ring.h>
 #include <net/inet_common.h>
 #include <linux/sched/signal.h>
@@ -134,6 +135,8 @@ static bool bpf_tcp_stream_read(const struct sock *sk)
 }
 
 static struct proto tcp_bpf_proto;
+static struct proto tcpv6_bpf_proto;
+
 static int bpf_tcp_init(struct sock *sk)
 {
 	struct smap_psock *psock;
@@ -154,13 +157,21 @@ static int bpf_tcp_init(struct sock *sk)
 	psock->sk_proto = sk->sk_prot;
 
 	if (psock->bpf_tx_msg) {
+		tcpv6_bpf_proto.sendmsg = bpf_tcp_sendmsg;
+		tcpv6_bpf_proto.sendpage = bpf_tcp_sendpage;
+		tcpv6_bpf_proto.recvmsg = bpf_tcp_recvmsg;
+		tcpv6_bpf_proto.stream_memory_read = bpf_tcp_stream_read;
 		tcp_bpf_proto.sendmsg = bpf_tcp_sendmsg;
 		tcp_bpf_proto.sendpage = bpf_tcp_sendpage;
 		tcp_bpf_proto.recvmsg = bpf_tcp_recvmsg;
 		tcp_bpf_proto.stream_memory_read = bpf_tcp_stream_read;
 	}
 
-	sk->sk_prot = &tcp_bpf_proto;
+	if (sk->sk_family == AF_INET6)
+		sk->sk_prot = &tcpv6_bpf_proto;
+	else
+		sk->sk_prot = &tcp_bpf_proto;
+
 	rcu_read_unlock();
 	return 0;
 }
@@ -1072,6 +1083,8 @@ static int bpf_tcp_ulp_register(void)
 {
 	tcp_bpf_proto = tcp_prot;
 	tcp_bpf_proto.close = bpf_tcp_close;
+	tcpv6_bpf_proto = tcpv6_prot;
+	tcpv6_bpf_proto.close = bpf_tcp_close;
 	/* Once BPF TX ULP is registered it is never unregistered. It
 	 * will be in the ULP list for the lifetime of the system. Doing
 	 * duplicate registers is not a problem.

^ permalink raw reply related

* Re: [PATCH net-next] rtnetlink: Fix null-ptr-deref in rtnl_newlink
From: Eric Dumazet @ 2018-06-01  8:26 UTC (permalink / raw)
  To: bhole_prashant_q7
  Cc: David Miller, Daniel Borkmann, Alexei Starovoitov, Kirill Tkhai,
	Florian Westphal, netdev, Kees Cook
In-Reply-To: <20180601081658.6968-1-bhole_prashant_q7@lab.ntt.co.jp>

On Fri, Jun 1, 2018 at 4:18 AM Prashant Bhole
<bhole_prashant_q7@lab.ntt.co.jp> wrote:
>
> In rtnl_newlink(), NULL check is performed on m_ops however member of
> ops is accessed. Fixed by accessing member of m_ops instead of ops.
>
> [  345.432629] BUG: KASAN: null-ptr-deref in rtnl_newlink+0x400/0x1110
> [  345.432629] Read of size 4 at addr 0000000000000088 by task ip/986
> [  345.432629]
> [  345.432629] CPU: 1 PID: 986 Comm: ip Not tainted 4.17.0-rc6+ #9
> [  345.432629] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
> [  345.432629] Call Trace:
> [  345.432629]  dump_stack+0xc6/0x150
> [  345.432629]  ? dump_stack_print_info.cold.0+0x1b/0x1b
> [  345.432629]  ? kasan_report+0xb4/0x410
> [  345.432629]  kasan_report.cold.4+0x8f/0x91
> [  345.432629]  ? rtnl_newlink+0x400/0x1110
> [  345.432629]  rtnl_newlink+0x400/0x1110
> [...]
>
> Fixes: ccf8dbcd062a ("rtnetlink: Remove VLA usage")
> Signed-off-by: Prashant Bhole <bhole_prashant_q7@lab.ntt.co.jp>
> ---
>  net/core/rtnetlink.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index 8ca49a0e13fb..9a1ba2015ad8 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -2936,7 +2936,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
>                 }
>
>                 if (m_ops) {
> -                       if (ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
> +                       if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
>                                 return -EINVAL;


Oh nice

CC Kees Cook.

^ permalink raw reply

* Re: [PATCH bpf 1/2] bpf: fix alignment of netns_dev/netns_ino fields in bpf_{map,prog}_info
From: Alexei Starovoitov @ 2018-06-01  8:41 UTC (permalink / raw)
  To: Linus Torvalds, Eugene Syromiatnikov, netdev, linux-kernel,
	Martin KaFai Lau, Daniel Borkmann, Alexei Starovoitov,
	David S. Miller, Jiri Olsa, Ingo Molnar, Lawrence Brakmo,
	Andrey Ignatov, Jakub Kicinski, John Fastabend
In-Reply-To: <20180601031210.GA30533@altlinux.org>

On Fri, Jun 01, 2018 at 06:12:10AM +0300, Dmitry V. Levin wrote:
> Hi,
> 
> Looks like the ABI bug in bpf_map_info and bpf_prog info introduced
> in 4.16 is going to slip into 4.17, causing extra pain to 32-bit
> userspace.  I'm adding Linus to this thread in hope it might help
> to get a fix applied before 4.17 is released.

The issue identified in patch 1 is valid.
These two fields won't be properly accessible by 32-bit user space
on 64-bit kernel.
But the fix in patch 1 is wrong.
The patch 2 is completely unnecessary.
Everyone can also see that the patches are still marked as 'new' in patchworks
meaning that we didn't process them.
At the moment many networking folks are traveling to netconf
and we only had a chance to discuss this set last night.
We'll try to send a fix in coming days.
But regardless whether 4.17 is realesed this sunday or not
we're not going to rush wrong patch in without proper code review
and discussion.
That future patch either will land in 4.17 (if it's dealyed into next sunday)
or it will be sent to stable.

To speed up the situation next time please report the issue that you find
to public netdev mailing list instead of using proprietary distro emails.

Thanks.

> On Wed, May 30, 2018 at 09:18:58PM +0300, Dmitry V. Levin wrote:
> > On Sun, May 27, 2018 at 01:28:42PM +0200, Eugene Syromiatnikov wrote:
> > > Recent introduction of netns_dev/netns_ino to bpf_map_info/bpf_prog info
> > > has broken compat, as offsets of these fields are different in 32-bit
> > > and 64-bit ABIs.  One fix (other than implementing compat support in
> > > syscall in order to handle this discrepancy) is to use __aligned_u64
> > > instead of __u64 for these fields.
> > > 
> > > Reported-by: Dmitry V. Levin <ldv@altlinux.org>
> > > Fixes: 52775b33bb507 ("bpf: offload: report device information about
> > > offloaded maps")
> > > Fixes: 675fc275a3a2d ("bpf: offload: report device information for
> > > offloaded programs")
> > > 
> > > Signed-off-by: Eugene Syromiatnikov <esyr@redhat.com>
> > 
> > Reviewed-by: "Dmitry V. Levin" <ldv@altlinux.org>
> > Cc: <stable@vger.kernel.org> # v4.16+
> > 
> > Thanks,
> > 
> > > ---
> > >  include/uapi/linux/bpf.h       | 8 ++++----
> > >  tools/include/uapi/linux/bpf.h | 8 ++++----
> > >  2 files changed, 8 insertions(+), 8 deletions(-)
> > > 
> > > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > > index c5ec897..903010a 100644
> > > --- a/include/uapi/linux/bpf.h
> > > +++ b/include/uapi/linux/bpf.h
> > > @@ -1017,8 +1017,8 @@ struct bpf_prog_info {
> > >  	__aligned_u64 map_ids;
> > >  	char name[BPF_OBJ_NAME_LEN];
> > >  	__u32 ifindex;
> > > -	__u64 netns_dev;
> > > -	__u64 netns_ino;
> > > +	__aligned_u64 netns_dev;
> > > +	__aligned_u64 netns_ino;
> > >  } __attribute__((aligned(8)));
> > >  
> > >  struct bpf_map_info {
> > > @@ -1030,8 +1030,8 @@ struct bpf_map_info {
> > >  	__u32 map_flags;
> > >  	char  name[BPF_OBJ_NAME_LEN];
> > >  	__u32 ifindex;
> > > -	__u64 netns_dev;
> > > -	__u64 netns_ino;
> > > +	__aligned_u64 netns_dev;
> > > +	__aligned_u64 netns_ino;
> > >  } __attribute__((aligned(8)));
> > >  
> > >  /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
> > > diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> > > index c5ec897..903010a 100644
> > > --- a/tools/include/uapi/linux/bpf.h
> > > +++ b/tools/include/uapi/linux/bpf.h
> > > @@ -1017,8 +1017,8 @@ struct bpf_prog_info {
> > >  	__aligned_u64 map_ids;
> > >  	char name[BPF_OBJ_NAME_LEN];
> > >  	__u32 ifindex;
> > > -	__u64 netns_dev;
> > > -	__u64 netns_ino;
> > > +	__aligned_u64 netns_dev;
> > > +	__aligned_u64 netns_ino;
> > >  } __attribute__((aligned(8)));
> > >  
> > >  struct bpf_map_info {
> > > @@ -1030,8 +1030,8 @@ struct bpf_map_info {
> > >  	__u32 map_flags;
> > >  	char  name[BPF_OBJ_NAME_LEN];
> > >  	__u32 ifindex;
> > > -	__u64 netns_dev;
> > > -	__u64 netns_ino;
> > > +	__aligned_u64 netns_dev;
> > > +	__aligned_u64 netns_ino;
> > >  } __attribute__((aligned(8)));
> > >  
> > >  /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
> 
> 
> -- 
> ldv

^ permalink raw reply

* Re: linux-next: build failure after merge of the net-next tree
From: Alexei Starovoitov @ 2018-06-01  8:52 UTC (permalink / raw)
  To: Stephen Rothwell
  Cc: David Miller, Networking, Linux-Next Mailing List,
	Linux Kernel Mailing List, Jakub Kicinski, Alexei Starovoitov,
	Daniel Borkmann
In-Reply-To: <20180601135943.66b90890@canb.auug.org.au>

On Fri, Jun 01, 2018 at 01:59:43PM +1000, Stephen Rothwell wrote:
> Hi all,
> 
> On Thu, 31 May 2018 07:38:55 +1000 Stephen Rothwell <sfr@canb.auug.org.au> wrote:
> >
> > On Tue, 29 May 2018 13:25:48 +1000 Stephen Rothwell <sfr@canb.auug.org.au> wrote:
> > >
> > > After merging the net-next tree, today's linux-next build (x86_64
> > > allmodconfig) failed like this:
> > > 
> > > x86_64-linux-ld: unknown architecture of input file `net/bpfilter/bpfilter_umh.o' is incompatible with i386:x86-64 output
> > > 
> > > Caused by commit
> > > 
> > >   d2ba09c17a06 ("net: add skeleton of bpfilter kernel module")
> > > 
> > > In my builds, the host is PowerPC 64 LE ...
> > > 
> > > I have reverted that commit along with
> > > 
> > >   61a552eb487f ("bpfilter: fix build dependency")
> > >   13405468f49d ("bpfilter: don't pass O_CREAT when opening console for debug")
> > > 
> > > for today.  
> > 
> > I am still getting this failure (well, at least yesterday I did).
> 
> Still happened today.  My guess is that bpfilter_umh needs to be built
> with the kernel compiler (not the host compiler - since ir is meant to
> run on the some machine as the kernel, right?) but will require the
> kernel architecture libc etc
> 
> 
> I replaced the reverts above with the following:
> 
> From: Stephen Rothwell <sfr@canb.auug.org.au>
> Date: Fri, 1 Jun 2018 13:33:28 +1000
> Subject: [PATCH] net: bpfilter: mark as BROKEN for now
> 
> This does not build in a cross compile environment
> 
> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
> ---
>  net/bpfilter/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/net/bpfilter/Kconfig b/net/bpfilter/Kconfig
> index a948b072c28f..ea4be72fdf6e 100644
> --- a/net/bpfilter/Kconfig
> +++ b/net/bpfilter/Kconfig
> @@ -2,6 +2,7 @@ menuconfig BPFILTER
>  	bool "BPF based packet filtering framework (BPFILTER)"
>  	default n
>  	depends on NET && BPF && INET
> +	depends on BROKEN

yeah. that's another option.
Sorry for delay. networking folks are traveling to netconf.
The issue is being discussed here:
https://patchwork.ozlabs.org/patch/921597/

Currently we're thinking how to add a check that hostcc's arch
is equal to cross-cc target arch and ideally equal to arch
of the host where it will be run.

^ permalink raw reply

* Re: [PATCH] net: core: improve the tx_hash calculating
From: Sergei Shtylyov @ 2018-06-01  8:58 UTC (permalink / raw)
  To: Tonghao Zhang, netdev
In-Reply-To: <1527761641-22034-1-git-send-email-xiangxia.m.yue@gmail.com>

Hello!

On 5/31/2018 1:14 PM, Tonghao Zhang wrote:

> Use the % instead of while, and it may simple code and improve
> the calculating. The real_num_tx_queues has been checked when
> allocating and setting it.
> 
> Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
> ---
>   net/core/dev.c | 8 +++-----
>   1 file changed, 3 insertions(+), 5 deletions(-)
> 
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 1844d9b..edc5b75 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -2617,15 +2617,13 @@ void netif_device_attach(struct net_device *dev)
>    */
>   static u16 skb_tx_hash(const struct net_device *dev, struct sk_buff *skb)
>   {
> -	u32 hash;
>   	u16 qoffset = 0;
>   	u16 qcount = dev->real_num_tx_queues;
>   
>   	if (skb_rx_queue_recorded(skb)) {
> -		hash = skb_get_rx_queue(skb);
> -		while (unlikely(hash >= qcount))
> -			hash -= qcount;
> -		return hash;
> +		/* When setting the real_num_tx_queues, we make sure
> +		 * real_num_tx_queues != 0. */

    In the networking code, multiline comments should look like:

/* bla
  * bla
  */

> +		return skb_get_rx_queue(skb) % qcount;
>   	}
>   
>   	if (dev->num_tc) {

MBR, Sergei

^ permalink raw reply

* Re: [PATCH 15/19] net: qualcomm: MODULE_DEVICE_TABLE(serdev)
From: Stefan Wahren @ 2018-06-01  8:59 UTC (permalink / raw)
  To: Ricardo Ribalda Delgado, linux-kernel, linux-serial
  Cc: Lino Sanfilippo, David S . Miller, Rob Herring, Johan Hovold,
	netdev
In-Reply-To: <20180529131014.18641-16-ricardo.ribalda@gmail.com>

Hi Ricardo,


Am 29.05.2018 um 15:10 schrieb Ricardo Ribalda Delgado:
> Export serdev table to the module header, allowing module autoload via
> udev/modprobe.
>
> Cc: Lino Sanfilippo <LinoSanfilippo@gmx.de>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: Stefan Wahren <stefan.wahren@i2se.com>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Johan Hovold <johan@kernel.org>
> Cc: netdev@vger.kernel.org
> Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
> ---
>   drivers/net/ethernet/qualcomm/qca_uart.c | 7 +++++++
>   1 file changed, 7 insertions(+)
>
> diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c
> index db6068cd7a1f..bb7aed805083 100644
> --- a/drivers/net/ethernet/qualcomm/qca_uart.c
> +++ b/drivers/net/ethernet/qualcomm/qca_uart.c
> @@ -405,6 +405,12 @@ static void qca_uart_remove(struct serdev_device *serdev)
>   	free_netdev(qca->net_dev);
>   }
>   
> +static struct serdev_device_id qca_uart_serdev_id[] = {
> +	{ QCAUART_DRV_NAME, },

i guess the id must be stable, so in case someone changes the driver 
name this has unexpected side effects?

Apart from that i'm fine with this patch.

Thanks
Stefan

> +	{},
> +};
> +MODULE_DEVICE_TABLE(serdev, qca_uart_serdev_id);
> +
>   static struct serdev_device_driver qca_uart_driver = {
>   	.probe = qca_uart_probe,
>   	.remove = qca_uart_remove,
> @@ -412,6 +418,7 @@ static struct serdev_device_driver qca_uart_driver = {
>   		.name = QCAUART_DRV_NAME,
>   		.of_match_table = of_match_ptr(qca_uart_of_match),
>   	},
> +	.id_table = qca_uart_serdev_id,
>   };
>   
>   module_serdev_device_driver(qca_uart_driver);

^ permalink raw reply

* Re: [PATCH net-next 1/5] net: aquantia: Ethtool based ring size configuration
From: Igor Russkikh @ 2018-06-01  9:19 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: David S . Miller, netdev, David Arcari, Pavel Belous,
	Anton Mikaev
In-Reply-To: <20180529120403.3f89a394@cakuba>


>> +
>> +	spin_lock(&aq_nic->aq_spinlock);
>> +
>> +	if (netif_running(ndev))
>> +		dev_close(ndev);
> 
> I don't think you can hold a spinlock around dev_close()/dev_open()
> calls.

Thanks Jakub, think you are right, will consider changing this lock to mutex.

>> +	if (!netif_running(ndev))
>> +		err = dev_open(ndev);
> 
> Will this not open the device regardless if it was open before or not?

Correct, thanks!

BR, Igor

^ permalink raw reply

* [RFC PATCH 00/16] bpf, bounded loop support work in progress
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev

This series is an early preview of ongoing work to support loops
inside BPF verifier. The code is rough in spots (more rough the
further you get into the series) but we have some signs of life!

The following code runs and is verified,

 SEC("classifier_tc_loop1")
 int _tc_loop(struct __sk_buff *ctx)
 {
	void *data      = (void *)(unsigned long)ctx->data;
	void *data_end  = (void *)(unsigned long)ctx->data_end;
	 __u8 i = 0, j = 0, k = 0, *p;

	 if (data + 2 > data_end)
		 return TC_ACT_OK;

	 p = data;
	 j = p[0];

	 if (data + 101 > data_end)
		 return TC_ACT_OK;

	 if (j < 100)
		 return TC_ACT_OK;

 #pragma nounroll
	while (i < j) {
		k += p[i];
		i++;
	}
	ctx->mark = k;

	return TC_ACT_OK;
 }

perhaps even more important many invalid loops are caught and
rejected. There are still a few cases that need to be handled
but largely things are working. And lots of code cleanup,
polishing and improvements sill needed but worth sharing
I think. Even if no one reads the code line by line the commit
messages have the description.

The series is broken into three parts.

Part1: Build the basic infrastruce. Basic Blocks, CFG and DOM are
built. (work done by Jiong).

Part2: Verify loops are bounded and encourage the state pruning
logic to prune as many states as possible.

Part3: Tests. Largely tbd at this point although I've been
doing lots of manual poking around with good/bad/ugly C programs.

Some high level details around design decisions for each part but
please see patch descriptions for more.

First Part1 Jiong chose to use Tarjan algorithm to build DOM.
(We also have an iterative solution if that is prefered but in
theory at least Tarjan has better running time.) Also we have
calculated the pdom as well but it is currently unused.

For all Part2 gory details please see patch,

 "bpf: verifier, add initial support to allow bounded loops"

The high level design is as follows,

  i. Use DOM to find loops
 ii. Find induction variables in loop
iii. Verify termination by comparing induction variable (inc/dec)
     against the JMP op. Propagate min/max values into insn aux
     data.
 iv. At do_check time ensure induction variable is bounded as
     required to ensure loop termination. These are a form of
     restriction on valid values for the registers.
  v. Encourage state pruning by using known bounds from
     previous induction step. e.g. if a scalar increments from
     0 to 100 we know min/max values that are vali for every
     iteration.
 vi. Loop state is usually pruned early but if not we can simply
     run the loop costing verifier complexity.

There is still lots of work to finish up all the pieces but we wanted
to share the current design.

Thanks,
John

---

Jiong Wang (11):
      bpf: cfg: partition basic blocks for each subprog
      bpf: cfg: add edges between basic blocks to form CFG
      bpf: cfg: build domination tree using Tarjan algorithm
      bpf: cfg: detect loop use domination information
      bpf: cfg: detect unreachable basic blocks
      bpf: cfg: move find_subprog/add_subprog to cfg.c
      bpf: cfg: build call graph and detect unreachable/recursive call
      bpf: cfg: remove push_insn and check_cfg
      bpf: cfg: reduce k*alloc/free call by using memory pool for allocating nodes
      bpf: cfg: reduce memory usage by using singly list + compat pointer
      bpf: cfg: detect irreducible loop using Eric Stoltz algorithm

John Fastabend (5):
      bpf: cfg: pretty print CFG and DOM
      bpf: verifier, can ptr range be calculated with scalar ALU op
      bpf: verifier, add initial support to allow bounded loops
      bpf: verifier, simple loop examples
      bpf: tools: dbg patch to turn on debugging and add primitive examples


 include/linux/bpf_verifier.h                   |   28 
 kernel/bpf/Makefile                            |    2 
 kernel/bpf/cfg.c                               | 2060 ++++++++++++++++++++++++
 kernel/bpf/cfg.h                               |   56 +
 kernel/bpf/verifier.c                          |  499 +++---
 samples/bpf/xdp2skb_meta_kern.c                |   57 +
 tools/lib/bpf/bpf.c                            |    2 
 tools/lib/bpf/libbpf.c                         |    7 
 tools/testing/selftests/bpf/Makefile           |    5 
 tools/testing/selftests/bpf/test_bad_loop1.c   |   47 +
 tools/testing/selftests/bpf/test_bad_loop2.c   |   45 +
 tools/testing/selftests/bpf/test_bad_loop3.c   |   47 +
 tools/testing/selftests/bpf/test_basic_loop1.c |   44 +
 tools/testing/selftests/bpf/test_basic_loop2.c |   48 +
 tools/testing/selftests/bpf/test_basic_loop3.c |   51 +
 15 files changed, 2711 insertions(+), 287 deletions(-)
 create mode 100644 kernel/bpf/cfg.c
 create mode 100644 kernel/bpf/cfg.h
 create mode 100644 tools/testing/selftests/bpf/test_bad_loop1.c
 create mode 100644 tools/testing/selftests/bpf/test_bad_loop2.c
 create mode 100644 tools/testing/selftests/bpf/test_bad_loop3.c
 create mode 100644 tools/testing/selftests/bpf/test_basic_loop1.c
 create mode 100644 tools/testing/selftests/bpf/test_basic_loop2.c
 create mode 100644 tools/testing/selftests/bpf/test_basic_loop3.c

^ permalink raw reply

* [RFC PATCH 01/16] bpf: cfg: partition basic blocks for each subprog
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

"check_subprogs" has partitioned subprogs and is doing insn scan for each
subprog already, this patch extends it to also partition basic blocks (BB)
for each subprog.

  - The first insn for each subprog start a BB.
  - Branch (both cond and absolute) target start a BB.
  - Insn immediately follows branch insn start a BB.
    Insn immediately follows exit and within subprog start a BB.

BBs for each subprog are organized as a list in ascending head.

Two special BBs, entry and exit are added as well.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 include/linux/bpf_verifier.h |    1 
 kernel/bpf/Makefile          |    2 -
 kernel/bpf/cfg.c             |   93 ++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/cfg.h             |   16 +++++++
 kernel/bpf/verifier.c        |   57 +++++++++++++++++++++++---
 5 files changed, 162 insertions(+), 7 deletions(-)
 create mode 100644 kernel/bpf/cfg.c
 create mode 100644 kernel/bpf/cfg.h

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 38b04f5..c70faf5 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -177,6 +177,7 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
 struct bpf_subprog_info {
 	u32 start; /* insn idx of function entry point */
 	u16 stack_depth; /* max. stack depth used by this function */
+	struct list_head bbs; /* basic blocks list. */
 };
 
 /* single container for all structs
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index f27f549..3ee9036 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-y := core.o
 
-obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
+obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cfg.o
 obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
 obj-$(CONFIG_BPF_SYSCALL) += disasm.o
 obj-$(CONFIG_BPF_SYSCALL) += btf.o
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
new file mode 100644
index 0000000..b1af714
--- /dev/null
+++ b/kernel/bpf/cfg.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2018 Netronome Systems, Inc. */
+/* This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#include <linux/bpf_verifier.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include "cfg.h"
+
+struct bb_node {
+	struct list_head l;
+	u16 head;
+};
+
+#define bb_prev(bb)		list_prev_entry(bb, l)
+#define bb_next(bb)		list_next_entry(bb, l)
+#define bb_first(bb_list)	list_first_entry(bb_list, struct bb_node, l)
+#define bb_last(bb_list)	list_last_entry(bb_list, struct bb_node, l)
+#define entry_bb(bb_list)	bb_first(bb_list)
+#define exit_bb(bb_list)	bb_last(bb_list)
+
+int subprog_append_bb(struct list_head *bb_list, int head)
+{
+	struct bb_node *new_bb, *bb;
+
+	list_for_each_entry(bb, bb_list, l) {
+		if (bb->head == head)
+			return 0;
+		else if (bb->head > head)
+			break;
+	}
+
+	bb = bb_prev(bb);
+	new_bb = kzalloc(sizeof(*new_bb), GFP_KERNEL);
+	if (!new_bb)
+		return -ENOMEM;
+
+	new_bb->head = head;
+	list_add(&new_bb->l, &bb->l);
+
+	return 0;
+}
+
+int subprog_fini_bb(struct list_head *bb_list, int subprog_end)
+{
+	struct bb_node *bb = kzalloc(sizeof(*bb), GFP_KERNEL);
+
+	if (!bb)
+		return -ENOMEM;
+	/* entry bb. */
+	bb->head = -1;
+	list_add(&bb->l, bb_list);
+
+	bb = kzalloc(sizeof(*bb), GFP_KERNEL);
+	if (!bb)
+		return -ENOMEM;
+	/* exit bb. */
+	bb->head = subprog_end;
+	list_add_tail(&bb->l, bb_list);
+
+	return 0;
+}
+
+int subprog_init_bb(struct list_head *bb_list, int subprog_start)
+{
+	int ret;
+
+	INIT_LIST_HEAD(bb_list);
+	ret = subprog_append_bb(bb_list, subprog_start);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx)
+{
+	int i = 0;
+
+	for (; i <= end_idx; i++) {
+		struct list_head *bbs = &subprog[i].bbs;
+		struct bb_node *bb, *tmp;
+
+		list_for_each_entry_safe(bb, tmp, bbs, l) {
+			list_del(&bb->l);
+			kfree(bb);
+		}
+	}
+}
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
new file mode 100644
index 0000000..4092145
--- /dev/null
+++ b/kernel/bpf/cfg.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2018 Netronome Systems, Inc. */
+/* This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#ifndef __BPF_CFG_H__
+#define __BPF_CFG_H__
+
+int subprog_append_bb(struct list_head *bb_list, int head);
+int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
+int subprog_init_bb(struct list_head *bb_list, int subprog_start);
+void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx);
+
+#endif
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1fd9667b..523e440 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -24,6 +24,7 @@
 #include <linux/sort.h>
 #include <linux/perf_event.h>
 
+#include "cfg.h"
 #include "disasm.h"
 
 static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
@@ -807,6 +808,7 @@ static int check_subprogs(struct bpf_verifier_env *env)
 	struct bpf_subprog_info *subprog = env->subprog_info;
 	struct bpf_insn *insn = env->prog->insnsi;
 	int insn_cnt = env->prog->len;
+	struct list_head *cur_bb_list;
 
 	/* Add entry function. */
 	ret = add_subprog(env, 0);
@@ -841,20 +843,46 @@ static int check_subprogs(struct bpf_verifier_env *env)
 		for (i = 0; i < env->subprog_cnt; i++)
 			verbose(env, "func#%d @%d\n", i, subprog[i].start);
 
-	/* now check that all jumps are within the same subprog */
 	subprog_start = subprog[cur_subprog].start;
 	subprog_end = subprog[cur_subprog + 1].start;
+	cur_bb_list = &subprog[cur_subprog].bbs;
+	ret = subprog_init_bb(cur_bb_list, subprog_start);
+	if (ret < 0)
+		goto free_nodes;
+	/* now check that all jumps are within the same subprog */
 	for (i = 0; i < insn_cnt; i++) {
 		u8 code = insn[i].code;
 
 		if (BPF_CLASS(code) != BPF_JMP)
 			goto next;
-		if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL)
+
+		if (BPF_OP(code) == BPF_EXIT) {
+			if (i + 1 < subprog_end) {
+				ret = subprog_append_bb(cur_bb_list, i + 1);
+				if (ret < 0)
+					goto free_nodes;
+			}
+			goto next;
+		}
+
+		if (BPF_OP(code) == BPF_CALL)
 			goto next;
+
 		off = i + insn[i].off + 1;
 		if (off < subprog_start || off >= subprog_end) {
 			verbose(env, "jump out of range from insn %d to %d\n", i, off);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_nodes;
+		}
+
+		ret = subprog_append_bb(cur_bb_list, off);
+		if (ret < 0)
+			goto free_nodes;
+
+		if (i + 1 < subprog_end) {
+			ret = subprog_append_bb(cur_bb_list, i + 1);
+			if (ret < 0)
+				goto free_nodes;
 		}
 next:
 		if (i == subprog_end - 1) {
@@ -865,15 +893,32 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			if (code != (BPF_JMP | BPF_EXIT) &&
 			    code != (BPF_JMP | BPF_JA)) {
 				verbose(env, "last insn is not an exit or jmp\n");
-				return -EINVAL;
+				ret = -EINVAL;
+				goto free_nodes;
 			}
+
+			ret = subprog_fini_bb(cur_bb_list, subprog_end);
+			if (ret < 0)
+				goto free_nodes;
 			subprog_start = subprog_end;
 			cur_subprog++;
-			if (cur_subprog < env->subprog_cnt)
+			if (cur_subprog < env->subprog_cnt) {
 				subprog_end = subprog[cur_subprog + 1].start;
+				cur_bb_list = &subprog[cur_subprog].bbs;
+				ret = subprog_init_bb(cur_bb_list,
+						      subprog_start);
+				if (ret < 0)
+					goto free_nodes;
+			}
 		}
 	}
-	return 0;
+
+	ret = 0;
+
+free_nodes:
+	subprog_free_bb(subprog, cur_subprog == env->subprog_cnt ?
+					cur_subprog - 1 : cur_subprog);
+	return ret;
 }
 
 static

^ permalink raw reply related

* [RFC PATCH 02/16] bpf: cfg: add edges between basic blocks to form CFG
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

This patch add edges between basic blocks. Both edges for predecessors and
successors are added.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 kernel/bpf/cfg.c      |  129 ++++++++++++++++++++++++++++++++++++++++++++++++-
 kernel/bpf/cfg.h      |    1 
 kernel/bpf/verifier.c |    3 +
 3 files changed, 131 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index b1af714..208aac7 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -11,8 +11,16 @@
 
 #include "cfg.h"
 
+struct edge_node {
+	struct list_head l;
+	struct bb_node *src;
+	struct bb_node *dst;
+};
+
 struct bb_node {
 	struct list_head l;
+	struct list_head e_prevs;
+	struct list_head e_succs;
 	u16 head;
 };
 
@@ -39,6 +47,8 @@ int subprog_append_bb(struct list_head *bb_list, int head)
 	if (!new_bb)
 		return -ENOMEM;
 
+	INIT_LIST_HEAD(&new_bb->e_prevs);
+	INIT_LIST_HEAD(&new_bb->e_succs);
 	new_bb->head = head;
 	list_add(&new_bb->l, &bb->l);
 
@@ -53,6 +63,8 @@ int subprog_fini_bb(struct list_head *bb_list, int subprog_end)
 		return -ENOMEM;
 	/* entry bb. */
 	bb->head = -1;
+	INIT_LIST_HEAD(&bb->e_prevs);
+	INIT_LIST_HEAD(&bb->e_succs);
 	list_add(&bb->l, bb_list);
 
 	bb = kzalloc(sizeof(*bb), GFP_KERNEL);
@@ -60,6 +72,8 @@ int subprog_fini_bb(struct list_head *bb_list, int subprog_end)
 		return -ENOMEM;
 	/* exit bb. */
 	bb->head = subprog_end;
+	INIT_LIST_HEAD(&bb->e_prevs);
+	INIT_LIST_HEAD(&bb->e_succs);
 	list_add_tail(&bb->l, bb_list);
 
 	return 0;
@@ -77,15 +91,126 @@ int subprog_init_bb(struct list_head *bb_list, int subprog_start)
 	return 0;
 }
 
+static struct bb_node *search_bb_with_head(struct list_head *bb_list, int head)
+{
+	struct bb_node *bb;
+
+	list_for_each_entry(bb, bb_list, l) {
+		if (bb->head == head)
+			return bb;
+	}
+
+	return NULL;
+}
+
+int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list)
+{
+	struct bb_node *bb, *exit_bb;
+	struct edge_node *edge;
+
+	bb = entry_bb(bb_list);
+	edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
+	if (!edge)
+		return -ENOMEM;
+	edge->src = bb;
+	edge->dst = bb_next(bb);
+	list_add_tail(&edge->l, &bb->e_succs);
+	edge[1].src = edge->src;
+	edge[1].dst = edge->dst;
+	list_add_tail(&edge[1].l, &edge[1].dst->e_prevs);
+
+	exit_bb = exit_bb(bb_list);
+	bb = bb_next(bb);
+	list_for_each_entry_from(bb, &exit_bb->l, l) {
+		bool has_fallthrough, only_has_fallthrough;
+		bool has_branch, only_has_branch;
+		struct bb_node *next_bb = bb_next(bb);
+		int tail = next_bb->head - 1;
+		struct bpf_insn insn;
+		u8 code;
+
+		edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
+		if (!edge)
+			return -ENOMEM;
+		edge->src = bb;
+		edge[1].src = bb;
+
+		insn = insns[tail];
+		code = insn.code;
+		only_has_fallthrough = BPF_CLASS(code) != BPF_JMP ||
+				       BPF_OP(code) == BPF_EXIT;
+		has_fallthrough = only_has_fallthrough ||
+				  (BPF_CLASS(code) == BPF_JMP &&
+				   BPF_OP(code) != BPF_CALL &&
+				   BPF_OP(code) != BPF_JA);
+		only_has_branch = BPF_CLASS(code) == BPF_JMP &&
+				  BPF_OP(code) == BPF_JA;
+		has_branch = only_has_branch ||
+			     (BPF_CLASS(code) == BPF_JMP &&
+			      BPF_OP(code) != BPF_EXIT &&
+			      BPF_OP(code) != BPF_CALL);
+
+		if (has_fallthrough) {
+			if (BPF_CLASS(code) == BPF_JMP &&
+			    BPF_OP(code) == BPF_EXIT)
+				next_bb = exit_bb;
+			edge->dst = next_bb;
+			edge[1].dst = next_bb;
+			list_add_tail(&edge->l, &bb->e_succs);
+			list_add_tail(&edge[1].l, &edge[1].dst->e_prevs);
+			edge = NULL;
+		}
+
+		if (has_branch) {
+			struct bb_node *tgt;
+
+			if (!edge) {
+				edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
+				if (!edge)
+					return -ENOMEM;
+				edge->src = bb;
+				edge[1].src = bb;
+			}
+
+			tgt = search_bb_with_head(bb_list,
+						  tail + insn.off + 1);
+			if (!tgt)
+				return -EINVAL;
+
+			edge->dst = tgt;
+			edge[1].dst = tgt;
+			list_add_tail(&edge->l, &bb->e_succs);
+			list_add_tail(&edge[1].l, &tgt->e_prevs);
+		}
+	}
+
+	return 0;
+}
+
+static void subprog_free_edge(struct bb_node *bb)
+{
+	struct list_head *succs = &bb->e_succs;
+	struct edge_node *e, *tmp;
+
+	/* prevs and succs are allocated as pair, succs is the start addr. */
+	list_for_each_entry_safe(e, tmp, succs, l) {
+		list_del(&e->l);
+		kfree(e);
+	}
+}
+
 void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx)
 {
 	int i = 0;
 
 	for (; i <= end_idx; i++) {
 		struct list_head *bbs = &subprog[i].bbs;
-		struct bb_node *bb, *tmp;
+		struct bb_node *bb, *tmp, *exit;
 
-		list_for_each_entry_safe(bb, tmp, bbs, l) {
+		bb = entry_bb(bbs);
+		exit = exit_bb(bbs);
+		list_for_each_entry_safe_from(bb, tmp, &exit->l, l) {
+			subprog_free_edge(bb);
 			list_del(&bb->l);
 			kfree(bb);
 		}
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index 4092145..7a9d5dd 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -8,6 +8,7 @@
 #ifndef __BPF_CFG_H__
 #define __BPF_CFG_H__
 
+int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list);
 int subprog_append_bb(struct list_head *bb_list, int head);
 int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
 int subprog_init_bb(struct list_head *bb_list, int subprog_start);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 523e440..bcac7c8 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -900,6 +900,9 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			ret = subprog_fini_bb(cur_bb_list, subprog_end);
 			if (ret < 0)
 				goto free_nodes;
+			ret = subprog_add_bb_edges(insn, cur_bb_list);
+			if (ret < 0)
+				goto free_nodes;
 			subprog_start = subprog_end;
 			cur_subprog++;
 			if (cur_subprog < env->subprog_cnt) {

^ permalink raw reply related

* [RFC PATCH 03/16] bpf: cfg: build domination tree using Tarjan algorithm
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

  - When building domination information, we need to some array data
    structure, and need to index into them using basic block index.
    Calculate the basic block index during adding edges.

  - The Step-1 in Tarjan algorithm is to assign dfs order number for
    each bb in the cfg. We need to iterate on all bbs in reverse dfs order.

  - Step-2 and Step-3 in Tarjan algorithm to calculate semi-dominators and
    immediate-dominators implicitly and explicitly.

  - Step-4, we add a new bitmap field inside subprog_info to keep the
    dominator tree there.

  - Post-domination information is built but not tested.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 include/linux/bpf_verifier.h |    3 
 kernel/bpf/cfg.c             |  386 ++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/cfg.h             |    3 
 kernel/bpf/verifier.c        |    5 -
 4 files changed, 393 insertions(+), 4 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index c70faf5..0eb838d 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -178,6 +178,9 @@ struct bpf_subprog_info {
 	u32 start; /* insn idx of function entry point */
 	u16 stack_depth; /* max. stack depth used by this function */
 	struct list_head bbs; /* basic blocks list. */
+	u32 bb_num; /* total basic block num. */
+	unsigned long *dtree;
+	bool dtree_avail;
 };
 
 /* single container for all structs
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 208aac7..b50937a 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -17,11 +17,33 @@ struct edge_node {
 	struct bb_node *dst;
 };
 
+struct edge_iter {
+	struct list_head *list_head;
+	struct edge_node *edge;
+};
+
+#define first_edge(e_list)	list_first_entry(e_list, struct edge_node, l)
+#define last_edge(e_list)	list_last_entry(e_list, struct edge_node, l)
+#define next_edge(e)		list_next_entry(e, l)
+
+static bool ei_end_p(struct edge_iter *ei)
+{
+	return &ei->edge->l == ei->list_head;
+}
+
+static void ei_next(struct edge_iter *ei)
+{
+	struct edge_node *e = ei->edge;
+
+	ei->edge = next_edge(e);
+}
+
 struct bb_node {
 	struct list_head l;
 	struct list_head e_prevs;
 	struct list_head e_succs;
 	u16 head;
+	u16 idx;
 };
 
 #define bb_prev(bb)		list_prev_entry(bb, l)
@@ -31,6 +53,27 @@ struct bb_node {
 #define entry_bb(bb_list)	bb_first(bb_list)
 #define exit_bb(bb_list)	bb_last(bb_list)
 
+struct dom_info {
+	u16 *dfs_parent;
+	u16 *dfs_order;
+	struct bb_node **dfs_to_bb;
+	/* immediate-dominator */
+	u16 *idom;
+	/* semi-dominator */
+	u16 *sdom;
+	u16 *bucket;
+	u16 *next_bucket;
+	/* best node during path compression. */
+	u16 *best;
+	/* ancestor along tree edge. */
+	u16 *ancestor;
+	/* size and child are used for tree balancing. */
+	u16 *size;
+	u16 *child;
+
+	u16 dfsnum;
+};
+
 int subprog_append_bb(struct list_head *bb_list, int head)
 {
 	struct bb_node *new_bb, *bb;
@@ -107,6 +150,7 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list)
 {
 	struct bb_node *bb, *exit_bb;
 	struct edge_node *edge;
+	int bb_num;
 
 	bb = entry_bb(bb_list);
 	edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
@@ -118,9 +162,12 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list)
 	edge[1].src = edge->src;
 	edge[1].dst = edge->dst;
 	list_add_tail(&edge[1].l, &edge[1].dst->e_prevs);
+	bb->idx = -1;
 
 	exit_bb = exit_bb(bb_list);
+	exit_bb->idx = -2;
 	bb = bb_next(bb);
+	bb_num = 0;
 	list_for_each_entry_from(bb, &exit_bb->l, l) {
 		bool has_fallthrough, only_has_fallthrough;
 		bool has_branch, only_has_branch;
@@ -129,6 +176,8 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list)
 		struct bpf_insn insn;
 		u8 code;
 
+		bb->idx = bb_num++;
+
 		edge = kcalloc(2, sizeof(*edge), GFP_KERNEL);
 		if (!edge)
 			return -ENOMEM;
@@ -184,9 +233,341 @@ int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list)
 		}
 	}
 
+	return bb_num + 2;
+}
+
+static int init_dom_info(struct bpf_subprog_info *subprog, struct dom_info *di)
+{
+	u16 *p, bb_num, i;
+
+	di->dfs_parent = NULL;
+	di->dfs_to_bb = NULL;
+
+	bb_num = subprog->bb_num;
+	p = kcalloc(10 * bb_num, sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	di->dfs_parent = p;
+	di->dfs_order = di->dfs_parent + bb_num;
+	di->idom = di->dfs_order + bb_num;
+	di->sdom = di->idom + bb_num;
+	di->bucket = di->sdom + bb_num;
+	di->next_bucket = di->bucket + bb_num;
+	di->best = di->next_bucket + bb_num;
+	di->ancestor = di->best + bb_num;
+	di->size = di->ancestor + bb_num;
+	di->child = di->size + bb_num;
+	di->dfs_to_bb = kcalloc(bb_num, sizeof(struct bb_node *), GFP_KERNEL);
+	di->dfsnum = 1;
+
+	for (i = 0; i < bb_num; i++) {
+		di->size[i] = 1;
+		di->best[i] = i;
+		di->sdom[i] = i;
+	}
+
 	return 0;
 }
 
+static void compress_path(struct dom_info *di, unsigned int v)
+{
+	unsigned int parent = di->ancestor[v];
+
+	if (di->ancestor[parent]) {
+		compress_path(di, parent);
+
+		if (di->sdom[di->best[parent]] < di->sdom[di->best[v]])
+			di->best[v] = di->best[parent];
+
+		di->ancestor[v] = di->ancestor[parent];
+	}
+}
+
+static unsigned int eval(struct dom_info *di, unsigned int v)
+{
+	unsigned int ancestor = di->ancestor[v];
+
+	/* v is root. */
+	if (!ancestor)
+		return di->best[v];
+
+	/* compress path */
+	compress_path(di, v);
+	ancestor = di->ancestor[v];
+
+	if (di->sdom[di->best[ancestor]] >= di->sdom[di->best[v]])
+		return di->best[v];
+	else
+		return di->best[ancestor];
+}
+
+/* Re-balancing the tree before linking. */
+static void link(struct dom_info *di, unsigned int v, unsigned int w)
+{
+	unsigned int s = w;
+
+	while (di->sdom[di->best[w]] < di->sdom[di->best[di->child[s]]]) {
+		if (di->size[s] + di->size[di->child[di->child[s]]] >=
+			2 * di->size[di->child[s]]) {
+			di->ancestor[di->child[s]] = s;
+			di->child[s] = di->child[di->child[s]];
+		} else {
+			di->size[di->child[s]] = di->size[s];
+			di->ancestor[s] = di->child[s];
+			s = di->child[s];
+		}
+	}
+
+	di->best[s] = di->best[w];
+	di->size[v] += di->size[w];
+	if (di->size[v] < 2 * di->size[w]) {
+		unsigned int t = s;
+
+		s = di->child[v];
+		di->child[v] = t;
+	}
+
+	while (s) {
+		di->ancestor[s] = v;
+		s = di->child[s];
+	}
+}
+
+static void calc_idoms(struct bpf_subprog_info *subprog, struct dom_info *di,
+		       bool reverse)
+{
+	u16 entry_bb_fake_idx = subprog->bb_num - 2, idx, w, k, par;
+	struct list_head *bb_list = &subprog->bbs;
+	struct bb_node *entry_bb;
+	struct edge_iter ei;
+
+	if (reverse)
+		entry_bb = exit_bb(bb_list);
+	else
+		entry_bb = entry_bb(bb_list);
+	idx = di->dfsnum - 1;
+
+	while (idx > 1) {
+		struct bb_node *bb = di->dfs_to_bb[idx];
+		struct edge_node *e;
+
+		par = di->dfs_parent[idx];
+		k = idx;
+
+		if (reverse) {
+			ei.edge = first_edge(&bb->e_succs);
+			ei.list_head = &bb->e_succs;
+		} else {
+			ei.edge = first_edge(&bb->e_prevs);
+			ei.list_head = &bb->e_prevs;
+		}
+
+		while (!ei_end_p(&ei)) {
+			struct bb_node *b;
+			u16 k1;
+
+			e = ei.edge;
+			if (reverse)
+				b = e->dst;
+			else
+				b = e->src;
+			ei_next(&ei);
+
+			if (b == entry_bb)
+				k1 = di->dfs_order[entry_bb_fake_idx];
+			else
+				k1 = di->dfs_order[b->idx];
+
+			if (k1 > idx)
+				k1 = di->sdom[eval(di, k1)];
+			if (k1 < k)
+				k = k1;
+		}
+
+		di->sdom[idx] = k;
+		link(di, par, idx);
+		di->next_bucket[idx] = di->bucket[k];
+		di->bucket[k] = idx;
+
+		for (w = di->bucket[par]; w; w = di->next_bucket[w]) {
+			k = eval(di, w);
+			if (di->sdom[k] < di->sdom[w])
+				di->idom[w] = k;
+			else
+				di->idom[w] = par;
+		}
+		di->bucket[par] = 0;
+		idx--;
+	}
+
+	di->idom[1] = 0;
+	for (idx = 2; idx <= di->dfsnum - 1; idx++)
+		if (di->idom[idx] != di->sdom[idx])
+			di->idom[idx] = di->idom[di->idom[idx]];
+}
+
+static int calc_dfs_tree(struct bpf_subprog_info *subprog, struct dom_info *di,
+			 bool reverse)
+{
+	u16 bb_num = subprog->bb_num, sp = 0, idx, parent_idx;
+	struct list_head *bb_list = &subprog->bbs;
+	u16 entry_bb_fake_idx = bb_num - 2;
+	struct bb_node *entry_bb, *exit_bb;
+	struct edge_iter ei, *stack;
+	struct edge_node *e;
+
+	di->dfs_order[entry_bb_fake_idx] = di->dfsnum;
+
+	stack = kmalloc_array(bb_num - 1, sizeof(struct edge_iter), GFP_KERNEL);
+	if (!stack)
+		return -ENOMEM;
+
+	if (reverse) {
+		entry_bb = exit_bb(bb_list);
+		exit_bb = entry_bb(bb_list);
+		di->dfs_to_bb[di->dfsnum++] = exit_bb;
+		ei.edge = first_edge(&entry_bb->e_prevs);
+		ei.list_head = &entry_bb->e_prevs;
+	} else {
+		entry_bb = entry_bb(bb_list);
+		exit_bb = exit_bb(bb_list);
+		di->dfs_to_bb[di->dfsnum++] = entry_bb;
+		ei.edge = first_edge(&entry_bb->e_succs);
+		ei.list_head = &entry_bb->e_succs;
+	}
+
+	while (1) {
+		struct bb_node *bb_dst, *bb_src;
+
+		while (!ei_end_p(&ei)) {
+			e = ei.edge;
+
+			if (reverse) {
+				bb_dst = e->src;
+				if (bb_dst == exit_bb ||
+				    di->dfs_order[bb_dst->idx]) {
+					ei_next(&ei);
+					continue;
+				}
+				bb_src = e->dst;
+			} else {
+				bb_dst = e->dst;
+				if (bb_dst == exit_bb ||
+				    di->dfs_order[bb_dst->idx]) {
+					ei_next(&ei);
+					continue;
+				}
+				bb_src = e->src;
+			}
+
+			if (bb_src != entry_bb)
+				parent_idx = di->dfs_order[bb_src->idx];
+			else
+				parent_idx = di->dfs_order[entry_bb_fake_idx];
+
+			idx = di->dfsnum++;
+			di->dfs_order[bb_dst->idx] = idx;
+			di->dfs_to_bb[idx] = bb_dst;
+			di->dfs_parent[idx] = parent_idx;
+
+			stack[sp++] = ei;
+			if (reverse) {
+				ei.edge = first_edge(&bb_dst->e_prevs);
+				ei.list_head = &bb_dst->e_prevs;
+			} else {
+				ei.edge = first_edge(&bb_dst->e_succs);
+				ei.list_head = &bb_dst->e_succs;
+			}
+		}
+
+		if (!sp)
+			break;
+
+		ei = stack[--sp];
+		ei_next(&ei);
+	}
+
+	kfree(stack);
+
+	return 0;
+}
+
+static int idoms_to_doms(struct bpf_subprog_info *subprog, struct dom_info *di)
+{
+	int bb_num, i, end_index, bb, bb_idom, lane_len;
+	unsigned long *bitmap;
+
+	bb_num = subprog->bb_num - 2;
+	lane_len = BITS_TO_LONGS(bb_num);
+	bitmap = kcalloc(bb_num, lane_len * sizeof(long), GFP_KERNEL);
+	subprog->dtree = bitmap;
+	if (!subprog->dtree)
+		return -ENOMEM;
+	subprog->dtree_avail = true;
+	end_index = di->dfs_order[bb_num];
+
+	for (i = 1; i <= di->dfsnum - 1; i++) {
+		if (i == end_index)
+			continue;
+
+		bb = di->dfs_to_bb[i]->idx;
+		if (di->idom[i] && di->idom[i] != end_index) {
+			bb_idom = di->dfs_to_bb[di->idom[i]]->idx;
+			bitmap_copy(bitmap + bb * lane_len,
+				    bitmap + bb_idom * lane_len, bb_num);
+		}
+
+		bitmap_set(bitmap + bb * lane_len, bb, 1);
+	}
+
+	return 0;
+}
+
+/* Build domination information using Lengauer and Tarjan algorithm.
+ *
+ *   1. dfs on cfg to assign dfs-num for each node(bb).
+ *   2. calculate semi-dominator and calculate immediate dominator when
+ *      possible.
+ *   3. calculate immediate dominator not finished during step 2.
+ *   4. build domination bitmap using immediate dominator information.
+ *
+ * See:
+ *   A fast algorithm for finding dominators in a flowgraph.
+ *     - 1979, by T Lengauer and R Tarjan
+ *
+ *   Especially, Appendix B: The Complete Dominators Algorithms.
+ *
+ * The implementation also referenced GNU GCC 3.0.
+ */
+
+int subprog_build_dom_info(struct bpf_subprog_info *subprog)
+{
+	struct dom_info di;
+	int ret;
+
+	ret = init_dom_info(subprog, &di);
+	if (ret < 0)
+		goto free_dominfo;
+
+	ret = calc_dfs_tree(subprog, &di, false);
+	if (ret < 0)
+		goto free_dominfo;
+
+	calc_idoms(subprog, &di, false);
+	ret = idoms_to_doms(subprog, &di);
+	if (ret < 0)
+		goto free_dominfo;
+
+	ret = 0;
+
+free_dominfo:
+	if (di.dfs_parent)
+		kfree(di.dfs_parent);
+
+	return ret;
+}
+
 static void subprog_free_edge(struct bb_node *bb)
 {
 	struct list_head *succs = &bb->e_succs;
@@ -199,7 +580,7 @@ static void subprog_free_edge(struct bb_node *bb)
 	}
 }
 
-void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx)
+void subprog_free(struct bpf_subprog_info *subprog, int end_idx)
 {
 	int i = 0;
 
@@ -214,5 +595,8 @@ void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx)
 			list_del(&bb->l);
 			kfree(bb);
 		}
+
+		if (subprog[i].dtree_avail)
+			kfree(subprog[i].dtree);
 	}
 }
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index 7a9d5dd..cbb44f2 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -10,8 +10,9 @@
 
 int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list);
 int subprog_append_bb(struct list_head *bb_list, int head);
+int subprog_build_dom_info(struct bpf_subprog_info *subprog);
 int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
 int subprog_init_bb(struct list_head *bb_list, int subprog_start);
-void subprog_free_bb(struct bpf_subprog_info *subprog, int end_idx);
+void subprog_free(struct bpf_subprog_info *subprog, int end_idx);
 
 #endif
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bcac7c8..eccaee4 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -903,6 +903,7 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			ret = subprog_add_bb_edges(insn, cur_bb_list);
 			if (ret < 0)
 				goto free_nodes;
+			subprog[cur_subprog].bb_num = ret;
 			subprog_start = subprog_end;
 			cur_subprog++;
 			if (cur_subprog < env->subprog_cnt) {
@@ -919,8 +920,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
 	ret = 0;
 
 free_nodes:
-	subprog_free_bb(subprog, cur_subprog == env->subprog_cnt ?
-					cur_subprog - 1 : cur_subprog);
+	subprog_free(subprog, cur_subprog == env->subprog_cnt ?
+				cur_subprog - 1 : cur_subprog);
 	return ret;
 }
 

^ permalink raw reply related

* [RFC PATCH 04/16] bpf: cfg: detect loop use domination information
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

If one bb is dominating its predecessor, then there is loop.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 kernel/bpf/cfg.c      |   22 ++++++++++++++++++++++
 kernel/bpf/cfg.h      |    1 +
 kernel/bpf/verifier.c |    8 ++++++++
 3 files changed, 31 insertions(+)

diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index b50937a..90692e4 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -568,6 +568,28 @@ int subprog_build_dom_info(struct bpf_subprog_info *subprog)
 	return ret;
 }
 
+bool subprog_has_loop(struct bpf_subprog_info *subprog)
+{
+	int lane_len = BITS_TO_LONGS(subprog->bb_num - 2);
+	struct list_head *bb_list = &subprog->bbs;
+	struct bb_node *bb, *entry_bb;
+	struct edge_node *e;
+
+	entry_bb = entry_bb(bb_list);
+	bb = bb_next(entry_bb);
+	list_for_each_entry_from(bb, &exit_bb(bb_list)->l, l)
+		list_for_each_entry(e, &bb->e_prevs, l) {
+			struct bb_node *latch = e->src;
+
+			if (latch != entry_bb &&
+			    test_bit(bb->idx,
+				     subprog->dtree + latch->idx * lane_len))
+				return true;
+		}
+
+	return false;
+}
+
 static void subprog_free_edge(struct bb_node *bb)
 {
 	struct list_head *succs = &bb->e_succs;
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index cbb44f2..c02c4cf 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -12,6 +12,7 @@
 int subprog_append_bb(struct list_head *bb_list, int head);
 int subprog_build_dom_info(struct bpf_subprog_info *subprog);
 int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
+bool subprog_has_loop(struct bpf_subprog_info *subprog);
 int subprog_init_bb(struct list_head *bb_list, int subprog_start);
 void subprog_free(struct bpf_subprog_info *subprog, int end_idx);
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index eccaee4..c349c45 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -904,6 +904,14 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			if (ret < 0)
 				goto free_nodes;
 			subprog[cur_subprog].bb_num = ret;
+			ret = subprog_build_dom_info(&subprog[cur_subprog]);
+			if (ret < 0)
+				goto free_nodes;
+			if (subprog_has_loop(&subprog[cur_subprog])) {
+				verbose(env, "cfg - loop detected");
+				ret = -EINVAL;
+				goto free_nodes;
+			}
 			subprog_start = subprog_end;
 			cur_subprog++;
 			if (cur_subprog < env->subprog_cnt) {

^ permalink raw reply related

* [RFC PATCH 05/16] bpf: cfg: detect unreachable basic blocks
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

Do unreachable basic blocks detection as a side-product of the dfs walk
when building domination information.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 kernel/bpf/cfg.c      |   19 ++++++++++++++-----
 kernel/bpf/cfg.h      |    3 ++-
 kernel/bpf/verifier.c |    3 ++-
 3 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 90692e4..2f0ac00 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -407,10 +407,11 @@ static void calc_idoms(struct bpf_subprog_info *subprog, struct dom_info *di,
 			di->idom[idx] = di->idom[di->idom[idx]];
 }
 
-static int calc_dfs_tree(struct bpf_subprog_info *subprog, struct dom_info *di,
-			 bool reverse)
+static int
+calc_dfs_tree(struct bpf_verifier_env *env, struct bpf_subprog_info *subprog,
+	      struct dom_info *di, bool reverse)
 {
-	u16 bb_num = subprog->bb_num, sp = 0, idx, parent_idx;
+	u16 bb_num = subprog->bb_num, sp = 0, idx, parent_idx, i;
 	struct list_head *bb_list = &subprog->bbs;
 	u16 entry_bb_fake_idx = bb_num - 2;
 	struct bb_node *entry_bb, *exit_bb;
@@ -490,6 +491,13 @@ static int calc_dfs_tree(struct bpf_subprog_info *subprog, struct dom_info *di,
 
 	kfree(stack);
 
+	for (i = 0; i < bb_num - 2; i++) {
+		if (!di->dfs_order[i]) {
+			bpf_verifier_log_write(env, "cfg - unreachable insn\n");
+			return -EINVAL;
+		}
+	}
+
 	return 0;
 }
 
@@ -541,7 +549,8 @@ static int idoms_to_doms(struct bpf_subprog_info *subprog, struct dom_info *di)
  * The implementation also referenced GNU GCC 3.0.
  */
 
-int subprog_build_dom_info(struct bpf_subprog_info *subprog)
+int subprog_build_dom_info(struct bpf_verifier_env *env,
+			   struct bpf_subprog_info *subprog)
 {
 	struct dom_info di;
 	int ret;
@@ -550,7 +559,7 @@ int subprog_build_dom_info(struct bpf_subprog_info *subprog)
 	if (ret < 0)
 		goto free_dominfo;
 
-	ret = calc_dfs_tree(subprog, &di, false);
+	ret = calc_dfs_tree(env, subprog, &di, false);
 	if (ret < 0)
 		goto free_dominfo;
 
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index c02c4cf..02729a9 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -10,7 +10,8 @@
 
 int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list);
 int subprog_append_bb(struct list_head *bb_list, int head);
-int subprog_build_dom_info(struct bpf_subprog_info *subprog);
+int subprog_build_dom_info(struct bpf_verifier_env *env,
+			   struct bpf_subprog_info *subprog);
 int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
 bool subprog_has_loop(struct bpf_subprog_info *subprog);
 int subprog_init_bb(struct list_head *bb_list, int subprog_start);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index c349c45..29797d2 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -904,7 +904,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			if (ret < 0)
 				goto free_nodes;
 			subprog[cur_subprog].bb_num = ret;
-			ret = subprog_build_dom_info(&subprog[cur_subprog]);
+			ret = subprog_build_dom_info(env,
+						     &subprog[cur_subprog]);
 			if (ret < 0)
 				goto free_nodes;
 			if (subprog_has_loop(&subprog[cur_subprog])) {

^ permalink raw reply related

* [RFC PATCH 06/16] bpf: cfg: move find_subprog/add_subprog to cfg.c
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

This patch centre find_subprog and add_subprog to cfg.c.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 kernel/bpf/cfg.c      |   41 +++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/cfg.h      |    2 ++
 kernel/bpf/verifier.c |   42 ------------------------------------------
 3 files changed, 43 insertions(+), 42 deletions(-)

diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 2f0ac00..5175aa7 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -6,8 +6,10 @@
  */
 
 #include <linux/bpf_verifier.h>
+#include <linux/bsearch.h>
 #include <linux/list.h>
 #include <linux/slab.h>
+#include <linux/sort.h>
 
 #include "cfg.h"
 
@@ -599,6 +601,45 @@ bool subprog_has_loop(struct bpf_subprog_info *subprog)
 	return false;
 }
 
+static int cmp_subprogs(const void *a, const void *b)
+{
+	return ((struct bpf_subprog_info *)a)->start -
+	       ((struct bpf_subprog_info *)b)->start;
+}
+
+int find_subprog(struct bpf_verifier_env *env, int off)
+{
+	struct bpf_subprog_info *p;
+
+	p = bsearch(&off, env->subprog_info, env->subprog_cnt,
+		    sizeof(env->subprog_info[0]), cmp_subprogs);
+	if (!p)
+		return -ENOENT;
+	return p - env->subprog_info;
+}
+
+int add_subprog(struct bpf_verifier_env *env, int off)
+{
+	int insn_cnt = env->prog->len;
+	int ret;
+
+	if (off >= insn_cnt || off < 0) {
+		bpf_verifier_log_write(env, "call to invalid destination\n");
+		return -EINVAL;
+	}
+	ret = find_subprog(env, off);
+	if (ret >= 0)
+		return 0;
+	if (env->subprog_cnt >= BPF_MAX_SUBPROGS) {
+		bpf_verifier_log_write(env, "too many subprograms\n");
+		return -E2BIG;
+	}
+	env->subprog_info[env->subprog_cnt++].start = off;
+	sort(env->subprog_info, env->subprog_cnt,
+	     sizeof(env->subprog_info[0]), cmp_subprogs, NULL);
+	return 0;
+}
+
 static void subprog_free_edge(struct bb_node *bb)
 {
 	struct list_head *succs = &bb->e_succs;
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index 02729a9..57eab9b 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -8,6 +8,8 @@
 #ifndef __BPF_CFG_H__
 #define __BPF_CFG_H__
 
+int add_subprog(struct bpf_verifier_env *env, int off);
+int find_subprog(struct bpf_verifier_env *env, int off);
 int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list);
 int subprog_append_bb(struct list_head *bb_list, int head);
 int subprog_build_dom_info(struct bpf_verifier_env *env,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 29797d2..782dd17 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -20,8 +20,6 @@
 #include <linux/file.h>
 #include <linux/vmalloc.h>
 #include <linux/stringify.h>
-#include <linux/bsearch.h>
-#include <linux/sort.h>
 #include <linux/perf_event.h>
 
 #include "cfg.h"
@@ -762,46 +760,6 @@ enum reg_arg_type {
 	DST_OP_NO_MARK	/* same as above, check only, don't mark */
 };
 
-static int cmp_subprogs(const void *a, const void *b)
-{
-	return ((struct bpf_subprog_info *)a)->start -
-	       ((struct bpf_subprog_info *)b)->start;
-}
-
-static int find_subprog(struct bpf_verifier_env *env, int off)
-{
-	struct bpf_subprog_info *p;
-
-	p = bsearch(&off, env->subprog_info, env->subprog_cnt,
-		    sizeof(env->subprog_info[0]), cmp_subprogs);
-	if (!p)
-		return -ENOENT;
-	return p - env->subprog_info;
-
-}
-
-static int add_subprog(struct bpf_verifier_env *env, int off)
-{
-	int insn_cnt = env->prog->len;
-	int ret;
-
-	if (off >= insn_cnt || off < 0) {
-		verbose(env, "call to invalid destination\n");
-		return -EINVAL;
-	}
-	ret = find_subprog(env, off);
-	if (ret >= 0)
-		return 0;
-	if (env->subprog_cnt >= BPF_MAX_SUBPROGS) {
-		verbose(env, "too many subprograms\n");
-		return -E2BIG;
-	}
-	env->subprog_info[env->subprog_cnt++].start = off;
-	sort(env->subprog_info, env->subprog_cnt,
-	     sizeof(env->subprog_info[0]), cmp_subprogs, NULL);
-	return 0;
-}
-
 static int check_subprogs(struct bpf_verifier_env *env)
 {
 	int i, ret, subprog_start, subprog_end, off, cur_subprog = 0;

^ permalink raw reply related

* [RFC PATCH 07/16] bpf: cfg: build call graph and detect unreachable/recursive call
From: John Fastabend @ 2018-06-01  9:32 UTC (permalink / raw)
  To: alexei.starovoitov, daniel, davem; +Cc: netdev
In-Reply-To: <20180601092646.15353.28269.stgit@john-Precision-Tower-5810>

From: Jiong Wang <jiong.wang@netronome.com>

This patch build call graph during insn scan inside check_subprogs.
Then do recursive and unreachable subprog detection using call graph.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 include/linux/bpf_verifier.h |    1 
 kernel/bpf/cfg.c             |  133 ++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/cfg.h             |    4 +
 kernel/bpf/verifier.c        |   32 +++++++++-
 4 files changed, 166 insertions(+), 4 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 0eb838d..83ccbc4 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -177,6 +177,7 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
 struct bpf_subprog_info {
 	u32 start; /* insn idx of function entry point */
 	u16 stack_depth; /* max. stack depth used by this function */
+	struct list_head callees; /* callees list. */
 	struct list_head bbs; /* basic blocks list. */
 	u32 bb_num; /* total basic block num. */
 	unsigned long *dtree;
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 5175aa7..56c08e8 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -13,6 +13,12 @@
 
 #include "cfg.h"
 
+struct cedge_node {
+	struct list_head l;
+	u8 caller_idx;
+	u8 callee_idx;
+};
+
 struct edge_node {
 	struct list_head l;
 	struct bb_node *src;
@@ -640,6 +646,126 @@ int add_subprog(struct bpf_verifier_env *env, int off)
 	return 0;
 }
 
+struct callee_iter {
+	struct list_head *list_head;
+	struct cedge_node *callee;
+};
+
+#define first_callee(c_list)	list_first_entry(c_list, struct cedge_node, l)
+#define next_callee(c)		list_next_entry(c, l)
+
+static bool ci_end_p(struct callee_iter *ci)
+{
+	return &ci->callee->l == ci->list_head;
+}
+
+static void ci_next(struct callee_iter *ci)
+{
+	struct cedge_node *c = ci->callee;
+
+	ci->callee = next_callee(c);
+}
+
+#define EXPLORING	1
+#define EXPLORED	2
+int cgraph_check_recursive_unreachable(struct bpf_verifier_env *env,
+				       struct bpf_subprog_info *subprog)
+{
+	struct callee_iter *stack;
+	struct cedge_node *callee;
+	int sp = 0, idx = 0, ret;
+	struct callee_iter ci;
+	int *status;
+
+	stack = kmalloc_array(128, sizeof(struct callee_iter), GFP_KERNEL);
+	if (!stack)
+		return -ENOMEM;
+	status = kcalloc(env->subprog_cnt, sizeof(int), GFP_KERNEL);
+	if (!status) {
+		kfree(stack);
+		return -ENOMEM;
+	}
+	ci.callee = first_callee(&subprog->callees);
+	ci.list_head = &subprog->callees;
+	status[0] = EXPLORING;
+
+	while (1) {
+		while (!ci_end_p(&ci)) {
+			callee = ci.callee;
+			idx = callee->callee_idx;
+			if (status[idx] == EXPLORING) {
+				bpf_verifier_log_write(env, "cgraph - recursive call\n");
+				ret = -EINVAL;
+				goto err_free;
+			}
+
+			status[idx] = EXPLORING;
+
+			if (sp == 127) {
+				bpf_verifier_log_write(env, "cgraph - call frame too deep\n");
+				ret = -EINVAL;
+				goto err_free;
+			}
+
+			stack[sp++] = ci;
+			ci.callee = first_callee(&subprog[idx].callees);
+			ci.list_head = &subprog[idx].callees;
+		}
+
+		if (!list_empty(ci.list_head))
+			status[first_callee(ci.list_head)->caller_idx] =
+				EXPLORED;
+		else
+			/* leaf func. */
+			status[idx] = EXPLORED;
+
+		if (!sp)
+			break;
+
+		ci = stack[--sp];
+		ci_next(&ci);
+	}
+
+	for (idx = 0; idx < env->subprog_cnt; idx++)
+		if (status[idx] != EXPLORED) {
+			bpf_verifier_log_write(env, "cgraph - unreachable subprog\n");
+			ret = -EINVAL;
+			goto err_free;
+		}
+
+	ret = 0;
+err_free:
+	kfree(status);
+	kfree(stack);
+	return ret;
+}
+
+int subprog_append_callee(struct bpf_verifier_env *env,
+			  struct list_head *callees_list,
+			  int caller_idx, int off)
+{
+	int callee_idx = find_subprog(env, off);
+	struct cedge_node *new_callee, *callee;
+
+	if (callee_idx < 0)
+		return callee_idx;
+
+	list_for_each_entry(callee, callees_list, l) {
+		if (callee->callee_idx == callee_idx)
+			return 0;
+	}
+
+	new_callee = kzalloc(sizeof(*new_callee), GFP_KERNEL);
+	if (!new_callee)
+		return -ENOMEM;
+
+	new_callee->caller_idx = caller_idx;
+	new_callee->callee_idx = callee_idx;
+	list_add_tail(&new_callee->l, callees_list);
+
+	return 0;
+}
+
 static void subprog_free_edge(struct bb_node *bb)
 {
 	struct list_head *succs = &bb->e_succs;
@@ -657,7 +783,9 @@ void subprog_free(struct bpf_subprog_info *subprog, int end_idx)
 	int i = 0;
 
 	for (; i <= end_idx; i++) {
+		struct list_head *callees = &subprog[i].callees;
 		struct list_head *bbs = &subprog[i].bbs;
+		struct cedge_node *callee, *tmp_callee;
 		struct bb_node *bb, *tmp, *exit;
 
 		bb = entry_bb(bbs);
@@ -668,6 +796,11 @@ void subprog_free(struct bpf_subprog_info *subprog, int end_idx)
 			kfree(bb);
 		}
 
+		list_for_each_entry_safe(callee, tmp_callee, callees, l) {
+			list_del(&callee->l);
+			kfree(callee);
+		}
+
 		if (subprog[i].dtree_avail)
 			kfree(subprog[i].dtree);
 	}
diff --git a/kernel/bpf/cfg.h b/kernel/bpf/cfg.h
index 57eab9b..577c22c 100644
--- a/kernel/bpf/cfg.h
+++ b/kernel/bpf/cfg.h
@@ -9,9 +9,13 @@
 #define __BPF_CFG_H__
 
 int add_subprog(struct bpf_verifier_env *env, int off);
+int cgraph_check_recursive_unreachable(struct bpf_verifier_env *env,
+				       struct bpf_subprog_info *subprog);
 int find_subprog(struct bpf_verifier_env *env, int off);
 int subprog_add_bb_edges(struct bpf_insn *insns, struct list_head *bb_list);
 int subprog_append_bb(struct list_head *bb_list, int head);
+int subprog_append_callee(struct bpf_verifier_env *env,
+			  struct list_head *bb_list, int caller_idx, int off);
 int subprog_build_dom_info(struct bpf_verifier_env *env,
 			   struct bpf_subprog_info *subprog);
 int subprog_fini_bb(struct list_head *bb_list, int subprog_end);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 782dd17..ddba8ad 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -764,9 +764,9 @@ static int check_subprogs(struct bpf_verifier_env *env)
 {
 	int i, ret, subprog_start, subprog_end, off, cur_subprog = 0;
 	struct bpf_subprog_info *subprog = env->subprog_info;
+	struct list_head *cur_bb_list, *cur_callee_list;
 	struct bpf_insn *insn = env->prog->insnsi;
 	int insn_cnt = env->prog->len;
-	struct list_head *cur_bb_list;
 
 	/* Add entry function. */
 	ret = add_subprog(env, 0);
@@ -797,16 +797,23 @@ static int check_subprogs(struct bpf_verifier_env *env)
 	 */
 	subprog[env->subprog_cnt].start = insn_cnt;
 
-	if (env->log.level > 1)
-		for (i = 0; i < env->subprog_cnt; i++)
+	for (i = 0; i < env->subprog_cnt; i++) {
+		if (env->log.level > 1)
 			verbose(env, "func#%d @%d\n", i, subprog[i].start);
 
+		/* Don't init callees inside add_subprog which will sort the
+		 * array which breaks list link.
+		 */
+		INIT_LIST_HEAD(&subprog[i].callees);
+	}
+
 	subprog_start = subprog[cur_subprog].start;
 	subprog_end = subprog[cur_subprog + 1].start;
 	cur_bb_list = &subprog[cur_subprog].bbs;
 	ret = subprog_init_bb(cur_bb_list, subprog_start);
 	if (ret < 0)
 		goto free_nodes;
+	cur_callee_list = &env->subprog_info[cur_subprog].callees;
 	/* now check that all jumps are within the same subprog */
 	for (i = 0; i < insn_cnt; i++) {
 		u8 code = insn[i].code;
@@ -823,8 +830,20 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			goto next;
 		}
 
-		if (BPF_OP(code) == BPF_CALL)
+		if (BPF_OP(code) == BPF_CALL) {
+			if (insn[i].src_reg == BPF_PSEUDO_CALL) {
+				int target = i + insn[i].imm + 1;
+
+				ret = subprog_append_callee(env,
+							    cur_callee_list,
+							    cur_subprog,
+							    target);
+				if (ret < 0)
+					return ret;
+			}
+
 			goto next;
+		}
 
 		off = i + insn[i].off + 1;
 		if (off < subprog_start || off >= subprog_end) {
@@ -880,10 +899,15 @@ static int check_subprogs(struct bpf_verifier_env *env)
 						      subprog_start);
 				if (ret < 0)
 					goto free_nodes;
+				cur_callee_list = &subprog[cur_subprog].callees;
 			}
 		}
 	}
 
+	ret = cgraph_check_recursive_unreachable(env, env->subprog_info);
+	if (ret < 0)
+		goto free_nodes;
+
 	ret = 0;
 
 free_nodes:

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox