summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/nfc/nci/rsp.c52
-rw-r--r--net/smc/smc_pnet.c20
-rw-r--r--net/xfrm/xfrm_nat_keepalive.c57
-rw-r--r--net/xfrm/xfrm_output.c4
4 files changed, 95 insertions, 38 deletions
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 6b2fa6bdbd14..b0ab4f5acbce 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -50,7 +50,8 @@ static u8 nci_core_init_rsp_packet_v1(struct nci_dev *ndev,
const struct nci_core_init_rsp_1 *rsp_1 = (void *)skb->data;
const struct nci_core_init_rsp_2 *rsp_2;
- if (skb->len < sizeof(*rsp_1))
+ /* Ensure that the status field can be accessed. */
+ if (skb_headlen(skb) < 1)
return NCI_STATUS_SYNTAX_ERROR;
pr_debug("status 0x%x\n", rsp_1->status);
@@ -58,15 +59,16 @@ static u8 nci_core_init_rsp_packet_v1(struct nci_dev *ndev,
if (rsp_1->status != NCI_STATUS_OK)
return rsp_1->status;
- /*
- * supported_rf_interfaces[] and the trailing nci_core_init_rsp_2 are
- * addressed using the on-wire (unclamped) interface count, so the
- * response must be long enough for both before any of it is parsed or
- * stored into @ndev - otherwise a truncated response would leave
- * ndev->num_supported_rf_interfaces holding the unclamped count.
+ /* Success response must contain the full fixed-size header */
+ if (skb_headlen(skb) < sizeof(*rsp_1))
+ return NCI_STATUS_SYNTAX_ERROR;
+
+ /* Ensure the variable-length rf_interfaces array and trailing
+ * rsp_2 structure are fully contained within the skb.
*/
- if (skb->len < sizeof(*rsp_1) +
- rsp_1->num_supported_rf_interfaces + sizeof(*rsp_2))
+ if (skb_headlen(skb) < sizeof(*rsp_1) +
+ rsp_1->num_supported_rf_interfaces +
+ sizeof(*rsp_2))
return NCI_STATUS_SYNTAX_ERROR;
ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features);
@@ -101,12 +103,12 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
const struct sk_buff *skb)
{
const struct nci_core_init_rsp_nci_ver2 *rsp = (void *)skb->data;
- const u8 *supported_rf_interface = rsp->supported_rf_interfaces;
- const u8 *end = skb->data + skb->len;
+ const u8 *supported_rf_interface;
u8 rf_interface_idx = 0;
u8 rf_extension_cnt = 0;
- if (skb->len < sizeof(*rsp))
+ /* Ensure that the status field can be accessed. */
+ if (skb_headlen(skb) < 1)
return NCI_STATUS_SYNTAX_ERROR;
pr_debug("status %x\n", rsp->status);
@@ -114,6 +116,12 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
if (rsp->status != NCI_STATUS_OK)
return rsp->status;
+ /* Success response must contain the full fixed-size header */
+ if (skb_headlen(skb) < sizeof(*rsp))
+ return NCI_STATUS_SYNTAX_ERROR;
+
+ supported_rf_interface = rsp->supported_rf_interfaces;
+
ndev->nfcc_features = __le32_to_cpu(rsp->nfcc_features);
ndev->num_supported_rf_interfaces = rsp->num_supported_rf_interfaces;
@@ -122,19 +130,22 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
NCI_MAX_SUPPORTED_RF_INTERFACES);
while (rf_interface_idx < ndev->num_supported_rf_interfaces) {
- /* one interface byte + one extension-count byte must be present */
- if (end - supported_rf_interface < 2)
- return NCI_STATUS_SYNTAX_ERROR;
- ndev->supported_rf_interfaces[rf_interface_idx++] =
- *supported_rf_interface++;
+ /* Each entry: [rf_interface_type (1B)] [ext_count (1B)] [ext...] */
+ if (supported_rf_interface + 2 > skb_tail_pointer(skb))
+ break;
+ ndev->supported_rf_interfaces[rf_interface_idx] = *supported_rf_interface++;
- /* skip rf extension parameters, bounded by the packet */
rf_extension_cnt = *supported_rf_interface++;
- if (rf_extension_cnt > end - supported_rf_interface)
- return NCI_STATUS_SYNTAX_ERROR;
+ if (supported_rf_interface + rf_extension_cnt > skb_tail_pointer(skb))
+ break;
+
+ /* Only count the entry after full validation */
+ rf_interface_idx++;
supported_rf_interface += rf_extension_cnt;
}
+ ndev->num_supported_rf_interfaces = rf_interface_idx;
+
ndev->max_logical_connections = rsp->max_logical_connections;
ndev->max_routing_table_size =
__le16_to_cpu(rsp->max_routing_table_size);
@@ -360,6 +371,7 @@ static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev,
list_del(&conn_info->list);
if (conn_info == ndev->rf_conn_info)
ndev->rf_conn_info = NULL;
+ devm_kfree(&ndev->nfc_dev->dev, conn_info->dest_params);
devm_kfree(&ndev->nfc_dev->dev, conn_info);
}
}
diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c
index 63e286e2dfaa..ff9c9c35cc2f 100644
--- a/net/smc/smc_pnet.c
+++ b/net/smc/smc_pnet.c
@@ -304,13 +304,18 @@ static bool smc_pnetid_valid(const char *pnet_name, char *pnetid)
return true;
}
-/* Find an infiniband device by a given name. The device might not exist. */
-static struct smc_ib_device *smc_pnet_find_ib(char *ib_name)
+/*
+ * Find an infiniband device by a given name, restricted to the devices
+ * accessible from @net. The device might not exist.
+ */
+static struct smc_ib_device *smc_pnet_find_ib(struct net *net, char *ib_name)
{
struct smc_ib_device *ibdev;
mutex_lock(&smc_ib_devices.mutex);
list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
+ if (!rdma_dev_access_netns(ibdev->ibdev, net))
+ continue;
if (!strncmp(ibdev->ibdev->name, ib_name,
sizeof(ibdev->ibdev->name)) ||
(ibdev->ibdev->dev.parent &&
@@ -408,8 +413,8 @@ out_put:
return rc;
}
-static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
- u8 ib_port, char *pnet_name)
+static int smc_pnet_add_ib(struct smc_pnettable *pnettable, struct net *net,
+ char *ib_name, u8 ib_port, char *pnet_name)
{
struct smc_pnetentry *tmp_pe, *new_pe;
struct smc_ib_device *ib_dev;
@@ -419,7 +424,7 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
bool new_ibdev;
/* try to apply the pnetid to active devices */
- ib_dev = smc_pnet_find_ib(ib_name);
+ ib_dev = smc_pnet_find_ib(net, ib_name);
if (ib_dev) {
ibdev_applied = smc_pnet_apply_ib(ib_dev, ib_port, pnet_name);
if (ibdev_applied)
@@ -518,7 +523,7 @@ static int smc_pnet_enter(struct net *net, struct nlattr *tb[])
if (ibport < 1 || ibport > SMC_MAX_PORTS)
goto error;
}
- rc = smc_pnet_add_ib(pnettable, string, ibport, pnet_name);
+ rc = smc_pnet_add_ib(pnettable, net, string, ibport, pnet_name);
if (!rc)
new_ibdev = true;
else if (rc != -EEXIST)
@@ -1170,6 +1175,9 @@ int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port)
struct smc_net *sn;
int rc = -ENOENT;
+ if (!rdma_dev_access_netns(smcibdev->ibdev, &init_net))
+ return -ENOENT;
+
/* get pnettable for init namespace */
sn = net_generic(&init_net, smc_net_id);
pnettable = &sn->pnettable;
diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c
index 0e21ab72f0fe..8d2e157a84e0 100644
--- a/net/xfrm/xfrm_nat_keepalive.c
+++ b/net/xfrm/xfrm_nat_keepalive.c
@@ -156,24 +156,51 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
}
struct nat_keepalive_work_ctx {
+ struct list_head states;
time64_t next_run;
time64_t now;
};
-static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr)
+struct nat_keepalive_state {
+ struct list_head list;
+ struct xfrm_state *x;
+};
+
+static int nat_keepalive_work_collect(struct xfrm_state *x, int count, void *ptr)
{
struct nat_keepalive_work_ctx *ctx = ptr;
+ struct nat_keepalive_state *state;
+
+ if (!READ_ONCE(x->nat_keepalive_interval))
+ return 0;
+
+ state = kmalloc_obj(*state, GFP_ATOMIC);
+ if (!state)
+ return -ENOMEM;
+
+ xfrm_state_hold(x);
+ state->x = x;
+ list_add_tail(&state->list, &ctx->states);
+ return 0;
+}
+
+static void nat_keepalive_work_single(struct xfrm_state *x,
+ struct nat_keepalive_work_ctx *ctx)
+{
bool send_keepalive = false;
struct nat_keepalive ka;
- time64_t next_run;
+ time64_t next_run = 0;
u32 interval;
int delta;
+ spin_lock_bh(&x->lock);
+
+ if (x->km.state == XFRM_STATE_DEAD)
+ goto out;
+
interval = x->nat_keepalive_interval;
if (!interval)
- return 0;
-
- spin_lock(&x->lock);
+ goto out;
delta = (int)(ctx->now - x->lastused);
if (delta < interval) {
@@ -187,29 +214,41 @@ static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr)
send_keepalive = true;
}
- spin_unlock(&x->lock);
+out:
+ spin_unlock_bh(&x->lock);
if (send_keepalive)
nat_keepalive_send(&ka);
- if (!ctx->next_run || next_run < ctx->next_run)
+ if (next_run && (!ctx->next_run || next_run < ctx->next_run))
ctx->next_run = next_run;
- return 0;
}
static void nat_keepalive_work(struct work_struct *work)
{
+ struct nat_keepalive_state *state, *tmp;
struct nat_keepalive_work_ctx ctx;
struct xfrm_state_walk walk;
struct net *net;
+ int err;
+ INIT_LIST_HEAD(&ctx.states);
ctx.next_run = 0;
ctx.now = ktime_get_real_seconds();
net = container_of(work, struct net, xfrm.nat_keepalive_work.work);
xfrm_state_walk_init(&walk, IPPROTO_ESP, NULL);
- xfrm_state_walk(net, &walk, nat_keepalive_work_single, &ctx);
+ err = xfrm_state_walk(net, &walk, nat_keepalive_work_collect, &ctx);
xfrm_state_walk_done(&walk, net);
+ list_for_each_entry_safe(state, tmp, &ctx.states, list) {
+ nat_keepalive_work_single(state->x, &ctx);
+ xfrm_state_put(state->x);
+ kfree(state);
+ }
+ if (err == -ENOMEM) {
+ schedule_delayed_work(&net->xfrm.nat_keepalive_work, 0);
+ return;
+ }
if (ctx.next_run)
schedule_delayed_work(&net->xfrm.nat_keepalive_work,
(ctx.next_run - ctx.now) * HZ);
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index cc35c2fcbbe0..e305ba32e356 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -636,10 +636,8 @@ static int xfrm_dev_direct_output(struct sock *sk, struct xfrm_state *x,
nf_reset_ct(skb);
err = skb_dst(skb)->ops->local_out(net, sk, skb);
- if (unlikely(err != 1)) {
- kfree_skb(skb);
+ if (unlikely(err != 1))
return err;
- }
/* In transport mode, network destination is
* directly reachable, while in tunnel mode,