diff options
author | Eric Biggers <ebiggers@google.com> | 2019-10-06 17:24:26 -0400 |
---|---|---|
committer | Jakub Kicinski <jakub.kicinski@netronome.com> | 2019-10-08 16:23:05 -0400 |
commit | fc8d5db10cbe1338a52ebc74e7feab9276721774 (patch) | |
tree | e86c4bac21ea8bd46b6bcc73db671e8fcfed9681 | |
parent | b74555de21acd791f12c4a1aeaf653dd7ac21133 (diff) |
llc: fix another potential sk_buff leak in llc_ui_sendmsg()
All callers of llc_conn_state_process() except llc_build_and_send_pkt()
(via llc_ui_sendmsg() -> llc_ui_send_data()) assume that it always
consumes a reference to the skb. Fix this caller to do the same.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
-rw-r--r-- | net/llc/af_llc.c | 34 | ||||
-rw-r--r-- | net/llc/llc_conn.c | 2 | ||||
-rw-r--r-- | net/llc/llc_if.c | 12 |
3 files changed, 30 insertions, 18 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 2017b7d780f5..c74f44dfaa22 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c | |||
@@ -113,22 +113,26 @@ static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr) | |||
113 | * | 113 | * |
114 | * Send data via reliable llc2 connection. | 114 | * Send data via reliable llc2 connection. |
115 | * Returns 0 upon success, non-zero if action did not succeed. | 115 | * Returns 0 upon success, non-zero if action did not succeed. |
116 | * | ||
117 | * This function always consumes a reference to the skb. | ||
116 | */ | 118 | */ |
117 | static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock) | 119 | static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock) |
118 | { | 120 | { |
119 | struct llc_sock* llc = llc_sk(sk); | 121 | struct llc_sock* llc = llc_sk(sk); |
120 | int rc = 0; | ||
121 | 122 | ||
122 | if (unlikely(llc_data_accept_state(llc->state) || | 123 | if (unlikely(llc_data_accept_state(llc->state) || |
123 | llc->remote_busy_flag || | 124 | llc->remote_busy_flag || |
124 | llc->p_flag)) { | 125 | llc->p_flag)) { |
125 | long timeout = sock_sndtimeo(sk, noblock); | 126 | long timeout = sock_sndtimeo(sk, noblock); |
127 | int rc; | ||
126 | 128 | ||
127 | rc = llc_ui_wait_for_busy_core(sk, timeout); | 129 | rc = llc_ui_wait_for_busy_core(sk, timeout); |
130 | if (rc) { | ||
131 | kfree_skb(skb); | ||
132 | return rc; | ||
133 | } | ||
128 | } | 134 | } |
129 | if (unlikely(!rc)) | 135 | return llc_build_and_send_pkt(sk, skb); |
130 | rc = llc_build_and_send_pkt(sk, skb); | ||
131 | return rc; | ||
132 | } | 136 | } |
133 | 137 | ||
134 | static void llc_ui_sk_init(struct socket *sock, struct sock *sk) | 138 | static void llc_ui_sk_init(struct socket *sock, struct sock *sk) |
@@ -899,7 +903,7 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |||
899 | DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name); | 903 | DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name); |
900 | int flags = msg->msg_flags; | 904 | int flags = msg->msg_flags; |
901 | int noblock = flags & MSG_DONTWAIT; | 905 | int noblock = flags & MSG_DONTWAIT; |
902 | struct sk_buff *skb; | 906 | struct sk_buff *skb = NULL; |
903 | size_t size = 0; | 907 | size_t size = 0; |
904 | int rc = -EINVAL, copied = 0, hdrlen; | 908 | int rc = -EINVAL, copied = 0, hdrlen; |
905 | 909 | ||
@@ -908,10 +912,10 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |||
908 | lock_sock(sk); | 912 | lock_sock(sk); |
909 | if (addr) { | 913 | if (addr) { |
910 | if (msg->msg_namelen < sizeof(*addr)) | 914 | if (msg->msg_namelen < sizeof(*addr)) |
911 | goto release; | 915 | goto out; |
912 | } else { | 916 | } else { |
913 | if (llc_ui_addr_null(&llc->addr)) | 917 | if (llc_ui_addr_null(&llc->addr)) |
914 | goto release; | 918 | goto out; |
915 | addr = &llc->addr; | 919 | addr = &llc->addr; |
916 | } | 920 | } |
917 | /* must bind connection to sap if user hasn't done it. */ | 921 | /* must bind connection to sap if user hasn't done it. */ |
@@ -919,7 +923,7 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |||
919 | /* bind to sap with null dev, exclusive. */ | 923 | /* bind to sap with null dev, exclusive. */ |
920 | rc = llc_ui_autobind(sock, addr); | 924 | rc = llc_ui_autobind(sock, addr); |
921 | if (rc) | 925 | if (rc) |
922 | goto release; | 926 | goto out; |
923 | } | 927 | } |
924 | hdrlen = llc->dev->hard_header_len + llc_ui_header_len(sk, addr); | 928 | hdrlen = llc->dev->hard_header_len + llc_ui_header_len(sk, addr); |
925 | size = hdrlen + len; | 929 | size = hdrlen + len; |
@@ -928,12 +932,12 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |||
928 | copied = size - hdrlen; | 932 | copied = size - hdrlen; |
929 | rc = -EINVAL; | 933 | rc = -EINVAL; |
930 | if (copied < 0) | 934 | if (copied < 0) |
931 | goto release; | 935 | goto out; |
932 | release_sock(sk); | 936 | release_sock(sk); |
933 | skb = sock_alloc_send_skb(sk, size, noblock, &rc); | 937 | skb = sock_alloc_send_skb(sk, size, noblock, &rc); |
934 | lock_sock(sk); | 938 | lock_sock(sk); |
935 | if (!skb) | 939 | if (!skb) |
936 | goto release; | 940 | goto out; |
937 | skb->dev = llc->dev; | 941 | skb->dev = llc->dev; |
938 | skb->protocol = llc_proto_type(addr->sllc_arphrd); | 942 | skb->protocol = llc_proto_type(addr->sllc_arphrd); |
939 | skb_reserve(skb, hdrlen); | 943 | skb_reserve(skb, hdrlen); |
@@ -943,29 +947,31 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |||
943 | if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) { | 947 | if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) { |
944 | llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_mac, | 948 | llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_mac, |
945 | addr->sllc_sap); | 949 | addr->sllc_sap); |
950 | skb = NULL; | ||
946 | goto out; | 951 | goto out; |
947 | } | 952 | } |
948 | if (addr->sllc_test) { | 953 | if (addr->sllc_test) { |
949 | llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_mac, | 954 | llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_mac, |
950 | addr->sllc_sap); | 955 | addr->sllc_sap); |
956 | skb = NULL; | ||
951 | goto out; | 957 | goto out; |
952 | } | 958 | } |
953 | if (addr->sllc_xid) { | 959 | if (addr->sllc_xid) { |
954 | llc_build_and_send_xid_pkt(llc->sap, skb, addr->sllc_mac, | 960 | llc_build_and_send_xid_pkt(llc->sap, skb, addr->sllc_mac, |
955 | addr->sllc_sap); | 961 | addr->sllc_sap); |
962 | skb = NULL; | ||
956 | goto out; | 963 | goto out; |
957 | } | 964 | } |
958 | rc = -ENOPROTOOPT; | 965 | rc = -ENOPROTOOPT; |
959 | if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua)) | 966 | if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua)) |
960 | goto out; | 967 | goto out; |
961 | rc = llc_ui_send_data(sk, skb, noblock); | 968 | rc = llc_ui_send_data(sk, skb, noblock); |
969 | skb = NULL; | ||
962 | out: | 970 | out: |
963 | if (rc) { | 971 | kfree_skb(skb); |
964 | kfree_skb(skb); | 972 | if (rc) |
965 | release: | ||
966 | dprintk("%s: failed sending from %02X to %02X: %d\n", | 973 | dprintk("%s: failed sending from %02X to %02X: %d\n", |
967 | __func__, llc->laddr.lsap, llc->daddr.lsap, rc); | 974 | __func__, llc->laddr.lsap, llc->daddr.lsap, rc); |
968 | } | ||
969 | release_sock(sk); | 975 | release_sock(sk); |
970 | return rc ? : copied; | 976 | return rc ? : copied; |
971 | } | 977 | } |
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index ed2aca12460c..0b0c6f12153b 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
@@ -55,6 +55,8 @@ int sysctl_llc2_busy_timeout = LLC2_BUSY_TIME * HZ; | |||
55 | * (executing it's actions and changing state), upper layer will be | 55 | * (executing it's actions and changing state), upper layer will be |
56 | * indicated or confirmed, if needed. Returns 0 for success, 1 for | 56 | * indicated or confirmed, if needed. Returns 0 for success, 1 for |
57 | * failure. The socket lock has to be held before calling this function. | 57 | * failure. The socket lock has to be held before calling this function. |
58 | * | ||
59 | * This function always consumes a reference to the skb. | ||
58 | */ | 60 | */ |
59 | int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | 61 | int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) |
60 | { | 62 | { |
diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c index 8db03c2d5440..ad6547736c21 100644 --- a/net/llc/llc_if.c +++ b/net/llc/llc_if.c | |||
@@ -38,6 +38,8 @@ | |||
38 | * closed and -EBUSY when sending data is not permitted in this state or | 38 | * closed and -EBUSY when sending data is not permitted in this state or |
39 | * LLC has send an I pdu with p bit set to 1 and is waiting for it's | 39 | * LLC has send an I pdu with p bit set to 1 and is waiting for it's |
40 | * response. | 40 | * response. |
41 | * | ||
42 | * This function always consumes a reference to the skb. | ||
41 | */ | 43 | */ |
42 | int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb) | 44 | int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb) |
43 | { | 45 | { |
@@ -46,20 +48,22 @@ int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb) | |||
46 | struct llc_sock *llc = llc_sk(sk); | 48 | struct llc_sock *llc = llc_sk(sk); |
47 | 49 | ||
48 | if (unlikely(llc->state == LLC_CONN_STATE_ADM)) | 50 | if (unlikely(llc->state == LLC_CONN_STATE_ADM)) |
49 | goto out; | 51 | goto out_free; |
50 | rc = -EBUSY; | 52 | rc = -EBUSY; |
51 | if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */ | 53 | if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */ |
52 | llc->p_flag)) { | 54 | llc->p_flag)) { |
53 | llc->failed_data_req = 1; | 55 | llc->failed_data_req = 1; |
54 | goto out; | 56 | goto out_free; |
55 | } | 57 | } |
56 | ev = llc_conn_ev(skb); | 58 | ev = llc_conn_ev(skb); |
57 | ev->type = LLC_CONN_EV_TYPE_PRIM; | 59 | ev->type = LLC_CONN_EV_TYPE_PRIM; |
58 | ev->prim = LLC_DATA_PRIM; | 60 | ev->prim = LLC_DATA_PRIM; |
59 | ev->prim_type = LLC_PRIM_TYPE_REQ; | 61 | ev->prim_type = LLC_PRIM_TYPE_REQ; |
60 | skb->dev = llc->dev; | 62 | skb->dev = llc->dev; |
61 | rc = llc_conn_state_process(sk, skb); | 63 | return llc_conn_state_process(sk, skb); |
62 | out: | 64 | |
65 | out_free: | ||
66 | kfree_skb(skb); | ||
63 | return rc; | 67 | return rc; |
64 | } | 68 | } |
65 | 69 | ||