Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c3063cc authored by Andreas Gruenbacher's avatar Andreas Gruenbacher Committed by Philipp Reisner
Browse files

drbd: Also need to check for DRBD_GENLA_F_MANDATORY flags before nla_find_nested()



This is done by introducing drbd_nla_find_nested() which handles the flag
before calling nla_find_nested().

Signed-off-by: default avatarPhilipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: default avatarLars Ellenberg <lars.ellenberg@linbit.com>
parent 789c1b62
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1407,6 +1407,12 @@ extern bool conn_try_outdate_peer(struct drbd_tconn *tconn);
extern void conn_try_outdate_peer_async(struct drbd_tconn *tconn);
extern int drbd_khelper(struct drbd_conf *mdev, char *cmd);

struct nla_policy;
extern int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla);
extern int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
				 const struct nla_policy *policy);
extern struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype);

/* drbd_worker.c */
extern int drbd_worker(struct drbd_thread *thi);
enum drbd_ret_code drbd_resync_after_valid(struct drbd_conf *mdev, int o_minor);
+76 −20
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ static struct drbd_config_context {
#define VOLUME_UNSPECIFIED		(-1U)
	/* pointer into the request skb,
	 * limited lifetime! */
	char *conn_name;
	char *resource_name;

	/* reply buffer */
	struct sk_buff *reply_skb;
@@ -191,15 +191,15 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
		/* and assign stuff to the global adm_ctx */
		nla = nested_attr_tb[__nla_type(T_ctx_volume)];
		adm_ctx.volume = nla ? nla_get_u32(nla) : VOLUME_UNSPECIFIED;
		nla = nested_attr_tb[__nla_type(T_ctx_conn_name)];
		nla = nested_attr_tb[__nla_type(T_ctx_resource_name)];
		if (nla)
			adm_ctx.conn_name = nla_data(nla);
			adm_ctx.resource_name = nla_data(nla);
	} else
		adm_ctx.volume = VOLUME_UNSPECIFIED;

	adm_ctx.minor = d_in->minor;
	adm_ctx.mdev = minor_to_mdev(d_in->minor);
	adm_ctx.tconn = conn_get_by_name(adm_ctx.conn_name);
	adm_ctx.tconn = conn_get_by_name(adm_ctx.resource_name);

	if (!adm_ctx.mdev && (flags & DRBD_ADM_NEED_MINOR)) {
		drbd_msg_put_info("unknown minor");
@@ -214,7 +214,8 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
	if (adm_ctx.mdev && adm_ctx.tconn &&
	    adm_ctx.mdev->tconn != adm_ctx.tconn) {
		pr_warning("request: minor=%u, conn=%s; but that minor belongs to connection %s\n",
				adm_ctx.minor, adm_ctx.conn_name, adm_ctx.mdev->tconn->name);
				adm_ctx.minor, adm_ctx.resource_name,
				adm_ctx.mdev->tconn->name);
		drbd_msg_put_info("minor exists in different connection");
		return ERR_INVALID_REQUEST;
	}
@@ -239,7 +240,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
static int drbd_adm_finish(struct genl_info *info, int retcode)
{
	struct nlattr *nla;
	const char *conn_name = NULL;
	const char *resource_name = NULL;

	if (adm_ctx.tconn) {
		kref_put(&adm_ctx.tconn->kref, &conn_destroy);
@@ -253,9 +254,10 @@ static int drbd_adm_finish(struct genl_info *info, int retcode)

	nla = info->attrs[DRBD_NLA_CFG_CONTEXT];
	if (nla) {
		nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name));
		if (nla)
			conn_name = nla_data(nla);
		int maxtype = ARRAY_SIZE(drbd_cfg_context_nl_policy) - 1;
		nla = drbd_nla_find_nested(maxtype, nla, __nla_type(T_ctx_resource_name));
		if (nla && !IS_ERR(nla))
			resource_name = nla_data(nla);
	}

	drbd_adm_send_reply(adm_ctx.reply_skb, info);
@@ -2526,7 +2528,7 @@ int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info)
	return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
}

int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *conn_name, unsigned vnr)
int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *resource_name, unsigned vnr)
{
	struct nlattr *nla;
	nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
@@ -2534,7 +2536,7 @@ int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *conn_name, unsigne
		goto nla_put_failure;
	if (vnr != VOLUME_UNSPECIFIED)
		NLA_PUT_U32(skb, T_ctx_volume, vnr);
	NLA_PUT_STRING(skb, T_ctx_conn_name, conn_name);
	NLA_PUT_STRING(skb, T_ctx_resource_name, resource_name);
	nla_nest_end(skb, nla);
	return 0;

@@ -2778,8 +2780,9 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
{
	const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ;
	struct nlattr *nla;
	const char *conn_name;
	const char *resource_name;
	struct drbd_tconn *tconn;
	int maxtype;

	/* Is this a followup call? */
	if (cb->args[0]) {
@@ -2799,12 +2802,15 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
	/* No explicit context given.  Dump all. */
	if (!nla)
		goto dump;
	nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name));
	maxtype = ARRAY_SIZE(drbd_cfg_context_nl_policy) - 1;
	nla = drbd_nla_find_nested(maxtype, nla, __nla_type(T_ctx_resource_name));
	if (IS_ERR(nla))
		return PTR_ERR(nla);
	/* context given, but no name present? */
	if (!nla)
		return -EINVAL;
	conn_name = nla_data(nla);
	tconn = conn_get_by_name(conn_name);
	resource_name = nla_data(nla);
	tconn = conn_get_by_name(resource_name);

	if (!tconn)
		return -ENODEV;
@@ -2957,16 +2963,16 @@ int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info)
}

static enum drbd_ret_code
drbd_check_conn_name(const char *name)
drbd_check_resource_name(const char *name)
{
	if (!name || !name[0]) {
		drbd_msg_put_info("connection name missing");
		drbd_msg_put_info("resource name missing");
		return ERR_MANDATORY_TAG;
	}
	/* if we want to use these in sysfs/configfs/debugfs some day,
	 * we must not allow slashes */
	if (strchr(name, '/')) {
		drbd_msg_put_info("invalid connection name");
		drbd_msg_put_info("invalid resource name");
		return ERR_INVALID_REQUEST;
	}
	return NO_ERROR;
@@ -2982,7 +2988,7 @@ int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info)
	if (retcode != NO_ERROR)
		goto out;

	retcode = drbd_check_conn_name(adm_ctx.conn_name);
	retcode = drbd_check_resource_name(adm_ctx.resource_name);
	if (retcode != NO_ERROR)
		goto out;

@@ -2995,7 +3001,7 @@ int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info)
		goto out;
	}

	if (!conn_create(adm_ctx.conn_name))
	if (!conn_create(adm_ctx.resource_name))
		retcode = ERR_NOMEM;
out:
	drbd_adm_finish(info, retcode);
@@ -3213,3 +3219,53 @@ void drbd_bcast_event(struct drbd_conf *mdev, const struct sib_info *sib)
			"Event seq:%u sib_reason:%u\n",
			err, seq, sib->sib_reason);
}

int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla)
{
	struct nlattr *head = nla_data(nla);
	int len = nla_len(nla);
	int rem;

	/*
	 * validate_nla (called from nla_parse_nested) ignores attributes
	 * beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag.
	 * In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY
	 * flag set also, check and remove that flag before calling
	 * nla_parse_nested.
	 */

	nla_for_each_attr(nla, head, len, rem) {
		if (nla->nla_type & DRBD_GENLA_F_MANDATORY) {
			nla->nla_type &= ~DRBD_GENLA_F_MANDATORY;
			if (nla_type(nla) > maxtype)
				return -EOPNOTSUPP;
		}
	}
	return 0;
}

int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
			  const struct nla_policy *policy)
{
	int err;

	err = drbd_nla_check_mandatory(maxtype, nla);
	if (!err)
		err = nla_parse_nested(tb, maxtype, nla, policy);

	return err;
}

struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype)
{
	int err;
	/*
	 * If any nested attribute has the DRBD_GENLA_F_MANDATORY flag set and
	 * we don't know about that attribute, reject all the nested
	 * attributes.
	 */
	err = drbd_nla_check_mandatory(maxtype, nla);
	if (err)
		return ERR_PTR(err);
	return nla_find_nested(nla, attrtype);
}
+1 −1
Original line number Diff line number Diff line
@@ -96,7 +96,7 @@ GENL_struct(DRBD_NLA_CFG_REPLY, 1, drbd_cfg_reply,
 * and the volume id within the resource. */
GENL_struct(DRBD_NLA_CFG_CONTEXT, 2, drbd_cfg_context,
	__u32_field(1, DRBD_GENLA_F_MANDATORY,	ctx_volume)
	__str_field(2, DRBD_GENLA_F_MANDATORY,	ctx_conn_name, 128)
	__str_field(2, DRBD_GENLA_F_MANDATORY,	ctx_resource_name, 128)
)

GENL_struct(DRBD_NLA_DISK_CONF, 3, disk_conf,
+0 −37
Original line number Diff line number Diff line
@@ -142,43 +142,6 @@ static struct nlattr *nested_attr_tb[128];
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
#endif

static inline int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla)
{
	struct nlattr *head = nla_data(nla);
	int len = nla_len(nla);
	int rem;

	/*
	 * validate_nla (called from nla_parse_nested) ignores attributes
	 * beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag.
	 * In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY
	 * flag set also, check and remove that flag before calling
	 * nla_parse_nested.
	 */

	nla_for_each_attr(nla, head, len, rem) {
		if (nla->nla_type & DRBD_GENLA_F_MANDATORY) {
			nla->nla_type &= ~DRBD_GENLA_F_MANDATORY;
			if (nla_type(nla) > maxtype)
				return -EOPNOTSUPP;
		}
	}
	return 0;
}

static inline int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype,
					struct nlattr *nla,
					const struct nla_policy *policy)
{
	int err;

	err = drbd_nla_check_mandatory(maxtype, nla);
	if (!err)
		err = nla_parse_nested(tb, maxtype, nla, policy);

	return err;
}

#undef GENL_struct
#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
/* *_from_attrs functions are static, but potentially unused */		\