diff options
author | Matthew Daley <mattjd@gmail.com> | 2011-10-14 14:45:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-10-17 19:31:39 -0400 |
commit | cb101ed2c3c7c0224d16953fe77bfb9d6c2cb9df (patch) | |
tree | 3d266ac18673ebc85a99e4d10d8d381ff1ebd782 /net/x25 | |
parent | c7fd0d48bde943e228e9c28ce971a22d6a1744c4 (diff) |
x25: Handle undersized/fragmented skbs
There are multiple locations in the X.25 packet layer where a skb is
assumed to be of at least a certain size and that all its data is
currently available at skb->data. These assumptions are not checked,
hence buffer overreads may occur. Use pskb_may_pull to check these
minimal size assumptions and ensure that data is available at skb->data
when necessary, as well as use skb_copy_bits where needed.
Signed-off-by: Matthew Daley <mattjd@gmail.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Andrew Hendry <andrew.hendry@gmail.com>
Cc: stable <stable@kernel.org>
Acked-by: Andrew Hendry <andrew.hendry@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/x25')
-rw-r--r-- | net/x25/af_x25.c | 31 | ||||
-rw-r--r-- | net/x25/x25_dev.c | 6 | ||||
-rw-r--r-- | net/x25/x25_facilities.c | 10 | ||||
-rw-r--r-- | net/x25/x25_in.c | 40 | ||||
-rw-r--r-- | net/x25/x25_link.c | 3 | ||||
-rw-r--r-- | net/x25/x25_subr.c | 14 |
6 files changed, 87 insertions, 17 deletions
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index a4bd1720e39b..aa567b09ea9a 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c | |||
@@ -91,7 +91,7 @@ int x25_parse_address_block(struct sk_buff *skb, | |||
91 | int needed; | 91 | int needed; |
92 | int rc; | 92 | int rc; |
93 | 93 | ||
94 | if (skb->len < 1) { | 94 | if (!pskb_may_pull(skb, 1)) { |
95 | /* packet has no address block */ | 95 | /* packet has no address block */ |
96 | rc = 0; | 96 | rc = 0; |
97 | goto empty; | 97 | goto empty; |
@@ -100,7 +100,7 @@ int x25_parse_address_block(struct sk_buff *skb, | |||
100 | len = *skb->data; | 100 | len = *skb->data; |
101 | needed = 1 + (len >> 4) + (len & 0x0f); | 101 | needed = 1 + (len >> 4) + (len & 0x0f); |
102 | 102 | ||
103 | if (skb->len < needed) { | 103 | if (!pskb_may_pull(skb, needed)) { |
104 | /* packet is too short to hold the addresses it claims | 104 | /* packet is too short to hold the addresses it claims |
105 | to hold */ | 105 | to hold */ |
106 | rc = -1; | 106 | rc = -1; |
@@ -951,10 +951,10 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, | |||
951 | * | 951 | * |
952 | * Facilities length is mandatory in call request packets | 952 | * Facilities length is mandatory in call request packets |
953 | */ | 953 | */ |
954 | if (skb->len < 1) | 954 | if (!pskb_may_pull(skb, 1)) |
955 | goto out_clear_request; | 955 | goto out_clear_request; |
956 | len = skb->data[0] + 1; | 956 | len = skb->data[0] + 1; |
957 | if (skb->len < len) | 957 | if (!pskb_may_pull(skb, len)) |
958 | goto out_clear_request; | 958 | goto out_clear_request; |
959 | skb_pull(skb,len); | 959 | skb_pull(skb,len); |
960 | 960 | ||
@@ -965,6 +965,13 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, | |||
965 | goto out_clear_request; | 965 | goto out_clear_request; |
966 | 966 | ||
967 | /* | 967 | /* |
968 | * Get all the call user data so it can be used in | ||
969 | * x25_find_listener and skb_copy_from_linear_data up ahead. | ||
970 | */ | ||
971 | if (!pskb_may_pull(skb, skb->len)) | ||
972 | goto out_clear_request; | ||
973 | |||
974 | /* | ||
968 | * Find a listener for the particular address/cud pair. | 975 | * Find a listener for the particular address/cud pair. |
969 | */ | 976 | */ |
970 | sk = x25_find_listener(&source_addr,skb); | 977 | sk = x25_find_listener(&source_addr,skb); |
@@ -1172,6 +1179,9 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
1172 | * byte of the user data is the logical value of the Q Bit. | 1179 | * byte of the user data is the logical value of the Q Bit. |
1173 | */ | 1180 | */ |
1174 | if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { | 1181 | if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { |
1182 | if (!pskb_may_pull(skb, 1)) | ||
1183 | goto out_kfree_skb; | ||
1184 | |||
1175 | qbit = skb->data[0]; | 1185 | qbit = skb->data[0]; |
1176 | skb_pull(skb, 1); | 1186 | skb_pull(skb, 1); |
1177 | } | 1187 | } |
@@ -1250,7 +1260,9 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1250 | struct x25_sock *x25 = x25_sk(sk); | 1260 | struct x25_sock *x25 = x25_sk(sk); |
1251 | struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; | 1261 | struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; |
1252 | size_t copied; | 1262 | size_t copied; |
1253 | int qbit; | 1263 | int qbit, header_len = x25->neighbour->extended ? |
1264 | X25_EXT_MIN_LEN : X25_STD_MIN_LEN; | ||
1265 | |||
1254 | struct sk_buff *skb; | 1266 | struct sk_buff *skb; |
1255 | unsigned char *asmptr; | 1267 | unsigned char *asmptr; |
1256 | int rc = -ENOTCONN; | 1268 | int rc = -ENOTCONN; |
@@ -1271,6 +1283,9 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1271 | 1283 | ||
1272 | skb = skb_dequeue(&x25->interrupt_in_queue); | 1284 | skb = skb_dequeue(&x25->interrupt_in_queue); |
1273 | 1285 | ||
1286 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) | ||
1287 | goto out_free_dgram; | ||
1288 | |||
1274 | skb_pull(skb, X25_STD_MIN_LEN); | 1289 | skb_pull(skb, X25_STD_MIN_LEN); |
1275 | 1290 | ||
1276 | /* | 1291 | /* |
@@ -1291,10 +1306,12 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
1291 | if (!skb) | 1306 | if (!skb) |
1292 | goto out; | 1307 | goto out; |
1293 | 1308 | ||
1309 | if (!pskb_may_pull(skb, header_len)) | ||
1310 | goto out_free_dgram; | ||
1311 | |||
1294 | qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; | 1312 | qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; |
1295 | 1313 | ||
1296 | skb_pull(skb, x25->neighbour->extended ? | 1314 | skb_pull(skb, header_len); |
1297 | X25_EXT_MIN_LEN : X25_STD_MIN_LEN); | ||
1298 | 1315 | ||
1299 | if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { | 1316 | if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { |
1300 | asmptr = skb_push(skb, 1); | 1317 | asmptr = skb_push(skb, 1); |
diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index e547ca1578c3..fa2b41888bd9 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c | |||
@@ -32,6 +32,9 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb) | |||
32 | unsigned short frametype; | 32 | unsigned short frametype; |
33 | unsigned int lci; | 33 | unsigned int lci; |
34 | 34 | ||
35 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) | ||
36 | return 0; | ||
37 | |||
35 | frametype = skb->data[2]; | 38 | frametype = skb->data[2]; |
36 | lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); | 39 | lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); |
37 | 40 | ||
@@ -115,6 +118,9 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev, | |||
115 | goto drop; | 118 | goto drop; |
116 | } | 119 | } |
117 | 120 | ||
121 | if (!pskb_may_pull(skb, 1)) | ||
122 | return 0; | ||
123 | |||
118 | switch (skb->data[0]) { | 124 | switch (skb->data[0]) { |
119 | 125 | ||
120 | case X25_IFACE_DATA: | 126 | case X25_IFACE_DATA: |
diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c index f77e4e75f914..36384a1fa9f2 100644 --- a/net/x25/x25_facilities.c +++ b/net/x25/x25_facilities.c | |||
@@ -44,7 +44,7 @@ | |||
44 | int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities, | 44 | int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities, |
45 | struct x25_dte_facilities *dte_facs, unsigned long *vc_fac_mask) | 45 | struct x25_dte_facilities *dte_facs, unsigned long *vc_fac_mask) |
46 | { | 46 | { |
47 | unsigned char *p = skb->data; | 47 | unsigned char *p; |
48 | unsigned int len; | 48 | unsigned int len; |
49 | 49 | ||
50 | *vc_fac_mask = 0; | 50 | *vc_fac_mask = 0; |
@@ -60,14 +60,16 @@ int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities, | |||
60 | memset(dte_facs->called_ae, '\0', sizeof(dte_facs->called_ae)); | 60 | memset(dte_facs->called_ae, '\0', sizeof(dte_facs->called_ae)); |
61 | memset(dte_facs->calling_ae, '\0', sizeof(dte_facs->calling_ae)); | 61 | memset(dte_facs->calling_ae, '\0', sizeof(dte_facs->calling_ae)); |
62 | 62 | ||
63 | if (skb->len < 1) | 63 | if (!pskb_may_pull(skb, 1)) |
64 | return 0; | 64 | return 0; |
65 | 65 | ||
66 | len = *p++; | 66 | len = skb->data[0]; |
67 | 67 | ||
68 | if (len >= skb->len) | 68 | if (!pskb_may_pull(skb, 1 + len)) |
69 | return -1; | 69 | return -1; |
70 | 70 | ||
71 | p = skb->data + 1; | ||
72 | |||
71 | while (len > 0) { | 73 | while (len > 0) { |
72 | switch (*p & X25_FAC_CLASS_MASK) { | 74 | switch (*p & X25_FAC_CLASS_MASK) { |
73 | case X25_FAC_CLASS_A: | 75 | case X25_FAC_CLASS_A: |
diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 63488fd4885a..a49cd4ec551a 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c | |||
@@ -107,6 +107,8 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
107 | /* | 107 | /* |
108 | * Parse the data in the frame. | 108 | * Parse the data in the frame. |
109 | */ | 109 | */ |
110 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) | ||
111 | goto out_clear; | ||
110 | skb_pull(skb, X25_STD_MIN_LEN); | 112 | skb_pull(skb, X25_STD_MIN_LEN); |
111 | 113 | ||
112 | len = x25_parse_address_block(skb, &source_addr, | 114 | len = x25_parse_address_block(skb, &source_addr, |
@@ -130,9 +132,8 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
130 | if (skb->len > X25_MAX_CUD_LEN) | 132 | if (skb->len > X25_MAX_CUD_LEN) |
131 | goto out_clear; | 133 | goto out_clear; |
132 | 134 | ||
133 | skb_copy_from_linear_data(skb, | 135 | skb_copy_bits(skb, 0, x25->calluserdata.cuddata, |
134 | x25->calluserdata.cuddata, | 136 | skb->len); |
135 | skb->len); | ||
136 | x25->calluserdata.cudlength = skb->len; | 137 | x25->calluserdata.cudlength = skb->len; |
137 | } | 138 | } |
138 | if (!sock_flag(sk, SOCK_DEAD)) | 139 | if (!sock_flag(sk, SOCK_DEAD)) |
@@ -140,6 +141,9 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
140 | break; | 141 | break; |
141 | } | 142 | } |
142 | case X25_CLEAR_REQUEST: | 143 | case X25_CLEAR_REQUEST: |
144 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) | ||
145 | goto out_clear; | ||
146 | |||
143 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); | 147 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
144 | x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); | 148 | x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); |
145 | break; | 149 | break; |
@@ -167,6 +171,9 @@ static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
167 | switch (frametype) { | 171 | switch (frametype) { |
168 | 172 | ||
169 | case X25_CLEAR_REQUEST: | 173 | case X25_CLEAR_REQUEST: |
174 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) | ||
175 | goto out_clear; | ||
176 | |||
170 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); | 177 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
171 | x25_disconnect(sk, 0, skb->data[3], skb->data[4]); | 178 | x25_disconnect(sk, 0, skb->data[3], skb->data[4]); |
172 | break; | 179 | break; |
@@ -180,6 +187,11 @@ static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
180 | } | 187 | } |
181 | 188 | ||
182 | return 0; | 189 | return 0; |
190 | |||
191 | out_clear: | ||
192 | x25_write_internal(sk, X25_CLEAR_REQUEST); | ||
193 | x25_start_t23timer(sk); | ||
194 | return 0; | ||
183 | } | 195 | } |
184 | 196 | ||
185 | /* | 197 | /* |
@@ -209,6 +221,9 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
209 | break; | 221 | break; |
210 | 222 | ||
211 | case X25_CLEAR_REQUEST: | 223 | case X25_CLEAR_REQUEST: |
224 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) | ||
225 | goto out_clear; | ||
226 | |||
212 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); | 227 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
213 | x25_disconnect(sk, 0, skb->data[3], skb->data[4]); | 228 | x25_disconnect(sk, 0, skb->data[3], skb->data[4]); |
214 | break; | 229 | break; |
@@ -307,6 +322,12 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
307 | } | 322 | } |
308 | 323 | ||
309 | return queued; | 324 | return queued; |
325 | |||
326 | out_clear: | ||
327 | x25_write_internal(sk, X25_CLEAR_REQUEST); | ||
328 | x25->state = X25_STATE_2; | ||
329 | x25_start_t23timer(sk); | ||
330 | return 0; | ||
310 | } | 331 | } |
311 | 332 | ||
312 | /* | 333 | /* |
@@ -316,13 +337,13 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
316 | */ | 337 | */ |
317 | static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) | 338 | static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) |
318 | { | 339 | { |
340 | struct x25_sock *x25 = x25_sk(sk); | ||
341 | |||
319 | switch (frametype) { | 342 | switch (frametype) { |
320 | 343 | ||
321 | case X25_RESET_REQUEST: | 344 | case X25_RESET_REQUEST: |
322 | x25_write_internal(sk, X25_RESET_CONFIRMATION); | 345 | x25_write_internal(sk, X25_RESET_CONFIRMATION); |
323 | case X25_RESET_CONFIRMATION: { | 346 | case X25_RESET_CONFIRMATION: { |
324 | struct x25_sock *x25 = x25_sk(sk); | ||
325 | |||
326 | x25_stop_timer(sk); | 347 | x25_stop_timer(sk); |
327 | x25->condition = 0x00; | 348 | x25->condition = 0x00; |
328 | x25->va = 0; | 349 | x25->va = 0; |
@@ -334,6 +355,9 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
334 | break; | 355 | break; |
335 | } | 356 | } |
336 | case X25_CLEAR_REQUEST: | 357 | case X25_CLEAR_REQUEST: |
358 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) | ||
359 | goto out_clear; | ||
360 | |||
337 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); | 361 | x25_write_internal(sk, X25_CLEAR_CONFIRMATION); |
338 | x25_disconnect(sk, 0, skb->data[3], skb->data[4]); | 362 | x25_disconnect(sk, 0, skb->data[3], skb->data[4]); |
339 | break; | 363 | break; |
@@ -343,6 +367,12 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp | |||
343 | } | 367 | } |
344 | 368 | ||
345 | return 0; | 369 | return 0; |
370 | |||
371 | out_clear: | ||
372 | x25_write_internal(sk, X25_CLEAR_REQUEST); | ||
373 | x25->state = X25_STATE_2; | ||
374 | x25_start_t23timer(sk); | ||
375 | return 0; | ||
346 | } | 376 | } |
347 | 377 | ||
348 | /* Higher level upcall for a LAPB frame */ | 378 | /* Higher level upcall for a LAPB frame */ |
diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index 037958ff8eed..4acacf3c6617 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c | |||
@@ -90,6 +90,9 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb, | |||
90 | break; | 90 | break; |
91 | 91 | ||
92 | case X25_DIAGNOSTIC: | 92 | case X25_DIAGNOSTIC: |
93 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 4)) | ||
94 | break; | ||
95 | |||
93 | printk(KERN_WARNING "x25: diagnostic #%d - %02X %02X %02X\n", | 96 | printk(KERN_WARNING "x25: diagnostic #%d - %02X %02X %02X\n", |
94 | skb->data[3], skb->data[4], | 97 | skb->data[3], skb->data[4], |
95 | skb->data[5], skb->data[6]); | 98 | skb->data[5], skb->data[6]); |
diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c index 24a342ebc7f5..5170d52bfd96 100644 --- a/net/x25/x25_subr.c +++ b/net/x25/x25_subr.c | |||
@@ -269,7 +269,11 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, | |||
269 | int *d, int *m) | 269 | int *d, int *m) |
270 | { | 270 | { |
271 | struct x25_sock *x25 = x25_sk(sk); | 271 | struct x25_sock *x25 = x25_sk(sk); |
272 | unsigned char *frame = skb->data; | 272 | unsigned char *frame; |
273 | |||
274 | if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) | ||
275 | return X25_ILLEGAL; | ||
276 | frame = skb->data; | ||
273 | 277 | ||
274 | *ns = *nr = *q = *d = *m = 0; | 278 | *ns = *nr = *q = *d = *m = 0; |
275 | 279 | ||
@@ -294,6 +298,10 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, | |||
294 | if (frame[2] == X25_RR || | 298 | if (frame[2] == X25_RR || |
295 | frame[2] == X25_RNR || | 299 | frame[2] == X25_RNR || |
296 | frame[2] == X25_REJ) { | 300 | frame[2] == X25_REJ) { |
301 | if (!pskb_may_pull(skb, X25_EXT_MIN_LEN)) | ||
302 | return X25_ILLEGAL; | ||
303 | frame = skb->data; | ||
304 | |||
297 | *nr = (frame[3] >> 1) & 0x7F; | 305 | *nr = (frame[3] >> 1) & 0x7F; |
298 | return frame[2]; | 306 | return frame[2]; |
299 | } | 307 | } |
@@ -308,6 +316,10 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, | |||
308 | 316 | ||
309 | if (x25->neighbour->extended) { | 317 | if (x25->neighbour->extended) { |
310 | if ((frame[2] & 0x01) == X25_DATA) { | 318 | if ((frame[2] & 0x01) == X25_DATA) { |
319 | if (!pskb_may_pull(skb, X25_EXT_MIN_LEN)) | ||
320 | return X25_ILLEGAL; | ||
321 | frame = skb->data; | ||
322 | |||
311 | *q = (frame[0] & X25_Q_BIT) == X25_Q_BIT; | 323 | *q = (frame[0] & X25_Q_BIT) == X25_Q_BIT; |
312 | *d = (frame[0] & X25_D_BIT) == X25_D_BIT; | 324 | *d = (frame[0] & X25_D_BIT) == X25_D_BIT; |
313 | *m = (frame[3] & X25_EXT_M_BIT) == X25_EXT_M_BIT; | 325 | *m = (frame[3] & X25_EXT_M_BIT) == X25_EXT_M_BIT; |