Skip to content

Commit aadb22b

Browse files
Yunlongsaxboe
authored andcommitted
drbd: Fix five use after free bugs in get_initial_state
In get_initial_state, it calls notify_initial_state_done(skb,..) if cb->args[5]==1. If genlmsg_put() failed in notify_initial_state_done(), the skb will be freed by nlmsg_free(skb). Then get_initial_state will goto out and the freed skb will be used by return value skb->len, which is a uaf bug. What's worse, the same problem goes even further: skb can also be freed in the notify_*_state_change -> notify_*_state calls below. Thus 4 additional uaf bugs happened. My patch lets the problem callee functions: notify_initial_state_done and notify_*_state_change return an error code if errors happen. So that the error codes could be propagated and the uaf bugs can be avoid. v2 reports a compilation warning. This v3 fixed this warning and built successfully in my local environment with no additional warnings. v2: https://lore.kernel.org/patchwork/patch/1435218/ Fixes: a297284 ("drbd: Backport the "events2" command") Signed-off-by: Lv Yunlong <lyl2019@mail.ustc.edu.cn> Reviewed-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 4ded53e commit aadb22b

File tree

4 files changed

+42
-33
lines changed

4 files changed

+42
-33
lines changed

drivers/block/drbd/drbd_int.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,22 +1638,22 @@ struct sib_info {
16381638
};
16391639
void drbd_bcast_event(struct drbd_device *device, const struct sib_info *sib);
16401640

1641-
extern void notify_resource_state(struct sk_buff *,
1641+
extern int notify_resource_state(struct sk_buff *,
16421642
unsigned int,
16431643
struct drbd_resource *,
16441644
struct resource_info *,
16451645
enum drbd_notification_type);
1646-
extern void notify_device_state(struct sk_buff *,
1646+
extern int notify_device_state(struct sk_buff *,
16471647
unsigned int,
16481648
struct drbd_device *,
16491649
struct device_info *,
16501650
enum drbd_notification_type);
1651-
extern void notify_connection_state(struct sk_buff *,
1651+
extern int notify_connection_state(struct sk_buff *,
16521652
unsigned int,
16531653
struct drbd_connection *,
16541654
struct connection_info *,
16551655
enum drbd_notification_type);
1656-
extern void notify_peer_device_state(struct sk_buff *,
1656+
extern int notify_peer_device_state(struct sk_buff *,
16571657
unsigned int,
16581658
struct drbd_peer_device *,
16591659
struct peer_device_info *,

drivers/block/drbd/drbd_nl.c

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4549,7 +4549,7 @@ static int nla_put_notification_header(struct sk_buff *msg,
45494549
return drbd_notification_header_to_skb(msg, &nh, true);
45504550
}
45514551

4552-
void notify_resource_state(struct sk_buff *skb,
4552+
int notify_resource_state(struct sk_buff *skb,
45534553
unsigned int seq,
45544554
struct drbd_resource *resource,
45554555
struct resource_info *resource_info,
@@ -4591,16 +4591,17 @@ void notify_resource_state(struct sk_buff *skb,
45914591
if (err && err != -ESRCH)
45924592
goto failed;
45934593
}
4594-
return;
4594+
return 0;
45954595

45964596
nla_put_failure:
45974597
nlmsg_free(skb);
45984598
failed:
45994599
drbd_err(resource, "Error %d while broadcasting event. Event seq:%u\n",
46004600
err, seq);
4601+
return err;
46014602
}
46024603

4603-
void notify_device_state(struct sk_buff *skb,
4604+
int notify_device_state(struct sk_buff *skb,
46044605
unsigned int seq,
46054606
struct drbd_device *device,
46064607
struct device_info *device_info,
@@ -4640,16 +4641,17 @@ void notify_device_state(struct sk_buff *skb,
46404641
if (err && err != -ESRCH)
46414642
goto failed;
46424643
}
4643-
return;
4644+
return 0;
46444645

46454646
nla_put_failure:
46464647
nlmsg_free(skb);
46474648
failed:
46484649
drbd_err(device, "Error %d while broadcasting event. Event seq:%u\n",
46494650
err, seq);
4651+
return err;
46504652
}
46514653

4652-
void notify_connection_state(struct sk_buff *skb,
4654+
int notify_connection_state(struct sk_buff *skb,
46534655
unsigned int seq,
46544656
struct drbd_connection *connection,
46554657
struct connection_info *connection_info,
@@ -4689,16 +4691,17 @@ void notify_connection_state(struct sk_buff *skb,
46894691
if (err && err != -ESRCH)
46904692
goto failed;
46914693
}
4692-
return;
4694+
return 0;
46934695

46944696
nla_put_failure:
46954697
nlmsg_free(skb);
46964698
failed:
46974699
drbd_err(connection, "Error %d while broadcasting event. Event seq:%u\n",
46984700
err, seq);
4701+
return err;
46994702
}
47004703

4701-
void notify_peer_device_state(struct sk_buff *skb,
4704+
int notify_peer_device_state(struct sk_buff *skb,
47024705
unsigned int seq,
47034706
struct drbd_peer_device *peer_device,
47044707
struct peer_device_info *peer_device_info,
@@ -4739,13 +4742,14 @@ void notify_peer_device_state(struct sk_buff *skb,
47394742
if (err && err != -ESRCH)
47404743
goto failed;
47414744
}
4742-
return;
4745+
return 0;
47434746

47444747
nla_put_failure:
47454748
nlmsg_free(skb);
47464749
failed:
47474750
drbd_err(peer_device, "Error %d while broadcasting event. Event seq:%u\n",
47484751
err, seq);
4752+
return err;
47494753
}
47504754

47514755
void notify_helper(enum drbd_notification_type type,
@@ -4796,7 +4800,7 @@ void notify_helper(enum drbd_notification_type type,
47964800
err, seq);
47974801
}
47984802

4799-
static void notify_initial_state_done(struct sk_buff *skb, unsigned int seq)
4803+
static int notify_initial_state_done(struct sk_buff *skb, unsigned int seq)
48004804
{
48014805
struct drbd_genlmsghdr *dh;
48024806
int err;
@@ -4810,11 +4814,12 @@ static void notify_initial_state_done(struct sk_buff *skb, unsigned int seq)
48104814
if (nla_put_notification_header(skb, NOTIFY_EXISTS))
48114815
goto nla_put_failure;
48124816
genlmsg_end(skb, dh);
4813-
return;
4817+
return 0;
48144818

48154819
nla_put_failure:
48164820
nlmsg_free(skb);
48174821
pr_err("Error %d sending event. Event seq:%u\n", err, seq);
4822+
return err;
48184823
}
48194824

48204825
static void free_state_changes(struct list_head *list)
@@ -4841,6 +4846,7 @@ static int get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)
48414846
unsigned int seq = cb->args[2];
48424847
unsigned int n;
48434848
enum drbd_notification_type flags = 0;
4849+
int err = 0;
48444850

48454851
/* There is no need for taking notification_mutex here: it doesn't
48464852
matter if the initial state events mix with later state chage
@@ -4849,32 +4855,32 @@ static int get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)
48494855

48504856
cb->args[5]--;
48514857
if (cb->args[5] == 1) {
4852-
notify_initial_state_done(skb, seq);
4858+
err = notify_initial_state_done(skb, seq);
48534859
goto out;
48544860
}
48554861
n = cb->args[4]++;
48564862
if (cb->args[4] < cb->args[3])
48574863
flags |= NOTIFY_CONTINUES;
48584864
if (n < 1) {
4859-
notify_resource_state_change(skb, seq, state_change->resource,
4865+
err = notify_resource_state_change(skb, seq, state_change->resource,
48604866
NOTIFY_EXISTS | flags);
48614867
goto next;
48624868
}
48634869
n--;
48644870
if (n < state_change->n_connections) {
4865-
notify_connection_state_change(skb, seq, &state_change->connections[n],
4871+
err = notify_connection_state_change(skb, seq, &state_change->connections[n],
48664872
NOTIFY_EXISTS | flags);
48674873
goto next;
48684874
}
48694875
n -= state_change->n_connections;
48704876
if (n < state_change->n_devices) {
4871-
notify_device_state_change(skb, seq, &state_change->devices[n],
4877+
err = notify_device_state_change(skb, seq, &state_change->devices[n],
48724878
NOTIFY_EXISTS | flags);
48734879
goto next;
48744880
}
48754881
n -= state_change->n_devices;
48764882
if (n < state_change->n_devices * state_change->n_connections) {
4877-
notify_peer_device_state_change(skb, seq, &state_change->peer_devices[n],
4883+
err = notify_peer_device_state_change(skb, seq, &state_change->peer_devices[n],
48784884
NOTIFY_EXISTS | flags);
48794885
goto next;
48804886
}
@@ -4889,7 +4895,10 @@ static int get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)
48894895
cb->args[4] = 0;
48904896
}
48914897
out:
4892-
return skb->len;
4898+
if (err)
4899+
return err;
4900+
else
4901+
return skb->len;
48934902
}
48944903

48954904
int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)

drivers/block/drbd/drbd_state.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ int drbd_bitmap_io_from_worker(struct drbd_device *device,
15371537
return rv;
15381538
}
15391539

1540-
void notify_resource_state_change(struct sk_buff *skb,
1540+
int notify_resource_state_change(struct sk_buff *skb,
15411541
unsigned int seq,
15421542
struct drbd_resource_state_change *resource_state_change,
15431543
enum drbd_notification_type type)
@@ -1550,10 +1550,10 @@ void notify_resource_state_change(struct sk_buff *skb,
15501550
.res_susp_fen = resource_state_change->susp_fen[NEW],
15511551
};
15521552

1553-
notify_resource_state(skb, seq, resource, &resource_info, type);
1553+
return notify_resource_state(skb, seq, resource, &resource_info, type);
15541554
}
15551555

1556-
void notify_connection_state_change(struct sk_buff *skb,
1556+
int notify_connection_state_change(struct sk_buff *skb,
15571557
unsigned int seq,
15581558
struct drbd_connection_state_change *connection_state_change,
15591559
enum drbd_notification_type type)
@@ -1564,10 +1564,10 @@ void notify_connection_state_change(struct sk_buff *skb,
15641564
.conn_role = connection_state_change->peer_role[NEW],
15651565
};
15661566

1567-
notify_connection_state(skb, seq, connection, &connection_info, type);
1567+
return notify_connection_state(skb, seq, connection, &connection_info, type);
15681568
}
15691569

1570-
void notify_device_state_change(struct sk_buff *skb,
1570+
int notify_device_state_change(struct sk_buff *skb,
15711571
unsigned int seq,
15721572
struct drbd_device_state_change *device_state_change,
15731573
enum drbd_notification_type type)
@@ -1577,10 +1577,10 @@ void notify_device_state_change(struct sk_buff *skb,
15771577
.dev_disk_state = device_state_change->disk_state[NEW],
15781578
};
15791579

1580-
notify_device_state(skb, seq, device, &device_info, type);
1580+
return notify_device_state(skb, seq, device, &device_info, type);
15811581
}
15821582

1583-
void notify_peer_device_state_change(struct sk_buff *skb,
1583+
int notify_peer_device_state_change(struct sk_buff *skb,
15841584
unsigned int seq,
15851585
struct drbd_peer_device_state_change *p,
15861586
enum drbd_notification_type type)
@@ -1594,15 +1594,15 @@ void notify_peer_device_state_change(struct sk_buff *skb,
15941594
.peer_resync_susp_dependency = p->resync_susp_dependency[NEW],
15951595
};
15961596

1597-
notify_peer_device_state(skb, seq, peer_device, &peer_device_info, type);
1597+
return notify_peer_device_state(skb, seq, peer_device, &peer_device_info, type);
15981598
}
15991599

16001600
static void broadcast_state_change(struct drbd_state_change *state_change)
16011601
{
16021602
struct drbd_resource_state_change *resource_state_change = &state_change->resource[0];
16031603
bool resource_state_has_changed;
16041604
unsigned int n_device, n_connection, n_peer_device, n_peer_devices;
1605-
void (*last_func)(struct sk_buff *, unsigned int, void *,
1605+
int (*last_func)(struct sk_buff *, unsigned int, void *,
16061606
enum drbd_notification_type) = NULL;
16071607
void *last_arg = NULL;
16081608

drivers/block/drbd/drbd_state_change.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,19 @@ extern struct drbd_state_change *remember_old_state(struct drbd_resource *, gfp_
4444
extern void copy_old_to_new_state_change(struct drbd_state_change *);
4545
extern void forget_state_change(struct drbd_state_change *);
4646

47-
extern void notify_resource_state_change(struct sk_buff *,
47+
extern int notify_resource_state_change(struct sk_buff *,
4848
unsigned int,
4949
struct drbd_resource_state_change *,
5050
enum drbd_notification_type type);
51-
extern void notify_connection_state_change(struct sk_buff *,
51+
extern int notify_connection_state_change(struct sk_buff *,
5252
unsigned int,
5353
struct drbd_connection_state_change *,
5454
enum drbd_notification_type type);
55-
extern void notify_device_state_change(struct sk_buff *,
55+
extern int notify_device_state_change(struct sk_buff *,
5656
unsigned int,
5757
struct drbd_device_state_change *,
5858
enum drbd_notification_type type);
59-
extern void notify_peer_device_state_change(struct sk_buff *,
59+
extern int notify_peer_device_state_change(struct sk_buff *,
6060
unsigned int,
6161
struct drbd_peer_device_state_change *,
6262
enum drbd_notification_type type);

0 commit comments

Comments
 (0)