diff options
author | Cong Wang <xiyou.wangcong@gmail.com> | 2018-03-26 18:08:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-03-27 11:56:00 -0400 |
commit | b85ab56c3f81c5a24b5a5213374f549df06430da (patch) | |
tree | 4580d5ab329020259c744ca5b5accae6997258c9 /net/llc | |
parent | 2a7fdec98f74cc305c1247cbe67307d504a3223d (diff) |
llc: properly handle dev_queue_xmit() return value
llc_conn_send_pdu() pushes the skb into write queue and
calls llc_conn_send_pdus() to flush them out. However, the
status of dev_queue_xmit() is not returned to caller,
in this case, llc_conn_state_process().
llc_conn_state_process() needs hold the skb no matter
success or failure, because it still uses it after that,
therefore we should hold skb before dev_queue_xmit() when
that skb is the one being processed by llc_conn_state_process().
For other callers, they can just pass NULL and ignore
the return value as they are.
Reported-by: Noam Rathaus <noamr@beyondsecurity.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/llc')
-rw-r--r-- | net/llc/llc_c_ac.c | 15 | ||||
-rw-r--r-- | net/llc/llc_conn.c | 32 |
2 files changed, 32 insertions, 15 deletions
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c index f59648018060..163121192aca 100644 --- a/net/llc/llc_c_ac.c +++ b/net/llc/llc_c_ac.c | |||
@@ -389,7 +389,7 @@ static int llc_conn_ac_send_i_cmd_p_set_0(struct sock *sk, struct sk_buff *skb) | |||
389 | llc_pdu_init_as_i_cmd(skb, 0, llc->vS, llc->vR); | 389 | llc_pdu_init_as_i_cmd(skb, 0, llc->vS, llc->vR); |
390 | rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac); | 390 | rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac); |
391 | if (likely(!rc)) { | 391 | if (likely(!rc)) { |
392 | llc_conn_send_pdu(sk, skb); | 392 | rc = llc_conn_send_pdu(sk, skb); |
393 | llc_conn_ac_inc_vs_by_1(sk, skb); | 393 | llc_conn_ac_inc_vs_by_1(sk, skb); |
394 | } | 394 | } |
395 | return rc; | 395 | return rc; |
@@ -916,7 +916,7 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk, | |||
916 | llc_pdu_init_as_i_cmd(skb, llc->ack_pf, llc->vS, llc->vR); | 916 | llc_pdu_init_as_i_cmd(skb, llc->ack_pf, llc->vS, llc->vR); |
917 | rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac); | 917 | rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac); |
918 | if (likely(!rc)) { | 918 | if (likely(!rc)) { |
919 | llc_conn_send_pdu(sk, skb); | 919 | rc = llc_conn_send_pdu(sk, skb); |
920 | llc_conn_ac_inc_vs_by_1(sk, skb); | 920 | llc_conn_ac_inc_vs_by_1(sk, skb); |
921 | } | 921 | } |
922 | return rc; | 922 | return rc; |
@@ -935,14 +935,17 @@ static int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk, | |||
935 | int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb) | 935 | int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb) |
936 | { | 936 | { |
937 | struct llc_sock *llc = llc_sk(sk); | 937 | struct llc_sock *llc = llc_sk(sk); |
938 | int ret; | ||
938 | 939 | ||
939 | if (llc->ack_must_be_send) { | 940 | if (llc->ack_must_be_send) { |
940 | llc_conn_ac_send_i_rsp_f_set_ackpf(sk, skb); | 941 | ret = llc_conn_ac_send_i_rsp_f_set_ackpf(sk, skb); |
941 | llc->ack_must_be_send = 0 ; | 942 | llc->ack_must_be_send = 0 ; |
942 | llc->ack_pf = 0; | 943 | llc->ack_pf = 0; |
943 | } else | 944 | } else { |
944 | llc_conn_ac_send_i_cmd_p_set_0(sk, skb); | 945 | ret = llc_conn_ac_send_i_cmd_p_set_0(sk, skb); |
945 | return 0; | 946 | } |
947 | |||
948 | return ret; | ||
946 | } | 949 | } |
947 | 950 | ||
948 | /** | 951 | /** |
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 9177dbb16dce..110e32bcb399 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
@@ -30,7 +30,7 @@ | |||
30 | #endif | 30 | #endif |
31 | 31 | ||
32 | static int llc_find_offset(int state, int ev_type); | 32 | static int llc_find_offset(int state, int ev_type); |
33 | static void llc_conn_send_pdus(struct sock *sk); | 33 | static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *skb); |
34 | static int llc_conn_service(struct sock *sk, struct sk_buff *skb); | 34 | static int llc_conn_service(struct sock *sk, struct sk_buff *skb); |
35 | static int llc_exec_conn_trans_actions(struct sock *sk, | 35 | static int llc_exec_conn_trans_actions(struct sock *sk, |
36 | struct llc_conn_state_trans *trans, | 36 | struct llc_conn_state_trans *trans, |
@@ -193,11 +193,11 @@ out_skb_put: | |||
193 | return rc; | 193 | return rc; |
194 | } | 194 | } |
195 | 195 | ||
196 | void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb) | 196 | int llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb) |
197 | { | 197 | { |
198 | /* queue PDU to send to MAC layer */ | 198 | /* queue PDU to send to MAC layer */ |
199 | skb_queue_tail(&sk->sk_write_queue, skb); | 199 | skb_queue_tail(&sk->sk_write_queue, skb); |
200 | llc_conn_send_pdus(sk); | 200 | return llc_conn_send_pdus(sk, skb); |
201 | } | 201 | } |
202 | 202 | ||
203 | /** | 203 | /** |
@@ -255,7 +255,7 @@ void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit) | |||
255 | if (howmany_resend > 0) | 255 | if (howmany_resend > 0) |
256 | llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO; | 256 | llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO; |
257 | /* any PDUs to re-send are queued up; start sending to MAC */ | 257 | /* any PDUs to re-send are queued up; start sending to MAC */ |
258 | llc_conn_send_pdus(sk); | 258 | llc_conn_send_pdus(sk, NULL); |
259 | out:; | 259 | out:; |
260 | } | 260 | } |
261 | 261 | ||
@@ -296,7 +296,7 @@ void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit) | |||
296 | if (howmany_resend > 0) | 296 | if (howmany_resend > 0) |
297 | llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO; | 297 | llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO; |
298 | /* any PDUs to re-send are queued up; start sending to MAC */ | 298 | /* any PDUs to re-send are queued up; start sending to MAC */ |
299 | llc_conn_send_pdus(sk); | 299 | llc_conn_send_pdus(sk, NULL); |
300 | out:; | 300 | out:; |
301 | } | 301 | } |
302 | 302 | ||
@@ -340,12 +340,16 @@ out: | |||
340 | /** | 340 | /** |
341 | * llc_conn_send_pdus - Sends queued PDUs | 341 | * llc_conn_send_pdus - Sends queued PDUs |
342 | * @sk: active connection | 342 | * @sk: active connection |
343 | * @hold_skb: the skb held by caller, or NULL if does not care | ||
343 | * | 344 | * |
344 | * Sends queued pdus to MAC layer for transmission. | 345 | * Sends queued pdus to MAC layer for transmission. When @hold_skb is |
346 | * NULL, always return 0. Otherwise, return 0 if @hold_skb is sent | ||
347 | * successfully, or 1 for failure. | ||
345 | */ | 348 | */ |
346 | static void llc_conn_send_pdus(struct sock *sk) | 349 | static int llc_conn_send_pdus(struct sock *sk, struct sk_buff *hold_skb) |
347 | { | 350 | { |
348 | struct sk_buff *skb; | 351 | struct sk_buff *skb; |
352 | int ret = 0; | ||
349 | 353 | ||
350 | while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) { | 354 | while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) { |
351 | struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb); | 355 | struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb); |
@@ -357,10 +361,20 @@ static void llc_conn_send_pdus(struct sock *sk) | |||
357 | skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb); | 361 | skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb); |
358 | if (!skb2) | 362 | if (!skb2) |
359 | break; | 363 | break; |
360 | skb = skb2; | 364 | dev_queue_xmit(skb2); |
365 | } else { | ||
366 | bool is_target = skb == hold_skb; | ||
367 | int rc; | ||
368 | |||
369 | if (is_target) | ||
370 | skb_get(skb); | ||
371 | rc = dev_queue_xmit(skb); | ||
372 | if (is_target) | ||
373 | ret = rc; | ||
361 | } | 374 | } |
362 | dev_queue_xmit(skb); | ||
363 | } | 375 | } |
376 | |||
377 | return ret; | ||
364 | } | 378 | } |
365 | 379 | ||
366 | /** | 380 | /** |