]>
Commit | Line | Data |
---|---|---|
6a930a95 BS |
1 | From: Chris Leech <christopher.leech@intel.com> |
2 | Subject: [FcOE] fix frame length validation in the early receive path | |
3 | References: bnc #459142 | |
4 | ||
5 | Validation of the frame length was missing before accessing the FC and FCoE | |
6 | headers. Some of the later checks were bogus, because of the way the fr_len | |
7 | variable and skb->len were being manipulated they could never fail. | |
8 | ||
9 | Signed-off-by: Chris Leech <christopher.leech@intel.com> | |
10 | Acked-by: Bernhard Walle <bwalle@suse.de> | |
11 | --- | |
12 | ||
13 | drivers/scsi/fcoe/libfcoe.c | 48 +++++++++++++++++++++----------------------- | |
14 | include/scsi/fc/fc_fcoe.h | 12 +++++++++++ | |
15 | include/scsi/fc_frame.h | 2 - | |
16 | 3 files changed, 36 insertions(+), 26 deletions(-) | |
17 | ||
18 | ||
19 | --- a/drivers/scsi/fcoe/libfcoe.c | |
20 | +++ b/drivers/scsi/fcoe/libfcoe.c | |
21 | @@ -184,7 +184,6 @@ int fcoe_rcv(struct sk_buff *skb, struct | |
22 | struct fcoe_rcv_info *fr; | |
23 | struct fcoe_softc *fc; | |
24 | struct fcoe_dev_stats *stats; | |
25 | - u8 *data; | |
26 | struct fc_frame_header *fh; | |
27 | unsigned short oxid; | |
28 | int cpu_idx; | |
29 | @@ -211,9 +210,18 @@ int fcoe_rcv(struct sk_buff *skb, struct | |
30 | FC_DBG("wrong FC type frame"); | |
31 | goto err; | |
32 | } | |
33 | - data = skb->data; | |
34 | - data += sizeof(struct fcoe_hdr); | |
35 | - fh = (struct fc_frame_header *)data; | |
36 | + | |
37 | + /* | |
38 | + * Check for minimum frame length, and make sure required FCoE | |
39 | + * and FC headers are pulled into the linear data area. | |
40 | + */ | |
41 | + if (unlikely((skb->len < FCOE_MIN_FRAME) || | |
42 | + !pskb_may_pull(skb, FCOE_HEADER_LEN))) | |
43 | + goto err; | |
44 | + | |
45 | + skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); | |
46 | + fh = (struct fc_frame_header *) skb_transport_header(skb); | |
47 | + | |
48 | oxid = ntohs(fh->fh_ox_id); | |
49 | ||
50 | fr = fcoe_dev_from_skb(skb); | |
51 | @@ -514,8 +522,6 @@ int fcoe_percpu_receive_thread(void *arg | |
52 | { | |
53 | struct fcoe_percpu_s *p = arg; | |
54 | u32 fr_len; | |
55 | - unsigned int hlen; | |
56 | - unsigned int tlen; | |
57 | struct fc_lport *lp; | |
58 | struct fcoe_rcv_info *fr; | |
59 | struct fcoe_dev_stats *stats; | |
60 | @@ -572,10 +578,12 @@ int fcoe_percpu_receive_thread(void *arg | |
61 | skb_linearize(skb); /* not ideal */ | |
62 | ||
63 | /* | |
64 | - * Check the header and pull it off. | |
65 | + * Frame length checks and setting up the header pointers | |
66 | + * was done in fcoe_rcv already. | |
67 | */ | |
68 | - hlen = sizeof(struct fcoe_hdr); | |
69 | - hp = (struct fcoe_hdr *)skb->data; | |
70 | + hp = (struct fcoe_hdr *) skb_network_header(skb); | |
71 | + fh = (struct fc_frame_header *) skb_transport_header(skb); | |
72 | + | |
73 | if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { | |
74 | if (stats) { | |
75 | if (stats->ErrorFrames < 5) | |
76 | @@ -586,22 +594,10 @@ int fcoe_percpu_receive_thread(void *arg | |
77 | kfree_skb(skb); | |
78 | continue; | |
79 | } | |
80 | + | |
81 | skb_pull(skb, sizeof(struct fcoe_hdr)); | |
82 | - tlen = sizeof(struct fcoe_crc_eof); | |
83 | - fr_len = skb->len - tlen; | |
84 | - skb_trim(skb, fr_len); | |
85 | + fr_len = skb->len - sizeof(struct fcoe_crc_eof); | |
86 | ||
87 | - if (unlikely(fr_len > skb->len)) { | |
88 | - if (stats) { | |
89 | - if (stats->ErrorFrames < 5) | |
90 | - FC_DBG("length error fr_len 0x%x " | |
91 | - "skb->len 0x%x", fr_len, | |
92 | - skb->len); | |
93 | - stats->ErrorFrames++; | |
94 | - } | |
95 | - kfree_skb(skb); | |
96 | - continue; | |
97 | - } | |
98 | if (stats) { | |
99 | stats->RxFrames++; | |
100 | stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; | |
101 | @@ -610,9 +606,11 @@ int fcoe_percpu_receive_thread(void *arg | |
102 | fp = (struct fc_frame *)skb; | |
103 | cp = (struct fcoe_crc_eof *)(skb->data + fr_len); | |
104 | fc_frame_init(fp); | |
105 | - fr_eof(fp) = cp->fcoe_eof; | |
106 | - fr_sof(fp) = hp->fcoe_sof; | |
107 | fr_dev(fp) = lp; | |
108 | + fr_sof(fp) = hp->fcoe_sof; | |
109 | + fr_eof(fp) = cp->fcoe_eof; | |
110 | + /* trim off the CRC and EOF trailer*/ | |
111 | + skb_trim(skb, fr_len); | |
112 | ||
113 | /* | |
114 | * We only check CRC if no offload is available and if it is | |
115 | --- a/include/scsi/fc/fc_fcoe.h | |
116 | +++ b/include/scsi/fc/fc_fcoe.h | |
117 | @@ -85,6 +85,18 @@ struct fcoe_crc_eof { | |
118 | } __attribute__((packed)); | |
119 | ||
120 | /* | |
121 | + * Minimum FCoE + FC header length | |
122 | + * 14 bytes FCoE header + 24 byte FC header = 38 bytes | |
123 | + */ | |
124 | +#define FCOE_HEADER_LEN 38 | |
125 | + | |
126 | +/* | |
127 | + * Minimum FCoE frame size | |
128 | + * 14 bytes FCoE header + 24 byte FC header + 8 byte FCoE trailer = 46 bytes | |
129 | + */ | |
130 | +#define FCOE_MIN_FRAME 46 | |
131 | + | |
132 | +/* | |
133 | * fc_fcoe_set_mac - Store OUI + DID into MAC address field. | |
134 | * @mac: mac address to be set | |
135 | * @did: fc dest id to use | |
136 | --- a/include/scsi/fc_frame.h | |
137 | +++ b/include/scsi/fc_frame.h | |
138 | @@ -66,10 +66,10 @@ struct fcoe_rcv_info { | |
139 | struct fc_lport *fr_dev; /* transport layer private pointer */ | |
140 | struct fc_seq *fr_seq; /* for use with exchange manager */ | |
141 | struct scsi_cmnd *fr_cmd; /* for use of scsi command */ | |
142 | + u16 fr_max_payload; /* max FC payload */ | |
143 | enum fc_sof fr_sof; /* start of frame delimiter */ | |
144 | enum fc_eof fr_eof; /* end of frame delimiter */ | |
145 | u8 fr_flags; /* flags - see below */ | |
146 | - u16 fr_max_payload; /* max FC payload */ | |
147 | }; | |
148 | ||
149 | /* |