diff options
author | Marko Kohtala <marko.kohtala@gmail.com> | 2006-01-06 03:19:43 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-06 11:33:56 -0500 |
commit | c660629059abbbd0eb56e12f9bb4494f01800bbc (patch) | |
tree | da6a8d3a38e0c2e91b45dda210dc9f56306027a0 /drivers/parport/probe.c | |
parent | 39ee059affaf57a152c64cd3a0adc3f48f02ed71 (diff) |
[PATCH] parport: buffer overflow fix
Fix potential buffer overflow in case the device ID did not end in semicolon.
Also might fail to negotiate back to IEEE1284_MODE_COMPAT in case of failure.
parport_device_id did not return what Documentation/parport-lowlevel.txt said,
so I changed it to match it.
Determining device ID length is overly complicated, but Tim Waugh recalled on
linux-parport seeing some buggy device that might need it.
Signed-off-by: Marko Kohtala <marko.kohtala@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/parport/probe.c')
-rw-r--r-- | drivers/parport/probe.c | 193 |
1 files changed, 130 insertions, 63 deletions
diff --git a/drivers/parport/probe.c b/drivers/parport/probe.c index 4b48b31ec235..5c29e8222211 100644 --- a/drivers/parport/probe.c +++ b/drivers/parport/probe.c | |||
@@ -128,8 +128,131 @@ static void parse_data(struct parport *port, int device, char *str) | |||
128 | kfree(txt); | 128 | kfree(txt); |
129 | } | 129 | } |
130 | 130 | ||
131 | /* Read up to count-1 bytes of device id. Terminate buffer with | ||
132 | * '\0'. Buffer begins with two Device ID length bytes as given by | ||
133 | * device. */ | ||
134 | static ssize_t parport_read_device_id (struct parport *port, char *buffer, | ||
135 | size_t count) | ||
136 | { | ||
137 | unsigned char length[2]; | ||
138 | unsigned lelen, belen; | ||
139 | size_t idlens[4]; | ||
140 | unsigned numidlens; | ||
141 | unsigned current_idlen; | ||
142 | ssize_t retval; | ||
143 | size_t len; | ||
144 | |||
145 | /* First two bytes are MSB,LSB of inclusive length. */ | ||
146 | retval = parport_read (port, length, 2); | ||
147 | |||
148 | if (retval < 0) | ||
149 | return retval; | ||
150 | if (retval != 2) | ||
151 | return -EIO; | ||
152 | |||
153 | if (count < 2) | ||
154 | return 0; | ||
155 | memcpy(buffer, length, 2); | ||
156 | len = 2; | ||
157 | |||
158 | /* Some devices wrongly send LE length, and some send it two | ||
159 | * bytes short. Construct a sorted array of lengths to try. */ | ||
160 | belen = (length[0] << 8) + length[1]; | ||
161 | lelen = (length[1] << 8) + length[0]; | ||
162 | idlens[0] = min(belen, lelen); | ||
163 | idlens[1] = idlens[0]+2; | ||
164 | if (belen != lelen) { | ||
165 | int off = 2; | ||
166 | /* Don't try lenghts of 0x100 and 0x200 as 1 and 2 */ | ||
167 | if (idlens[0] <= 2) | ||
168 | off = 0; | ||
169 | idlens[off] = max(belen, lelen); | ||
170 | idlens[off+1] = idlens[off]+2; | ||
171 | numidlens = off+2; | ||
172 | } | ||
173 | else { | ||
174 | /* Some devices don't truly implement Device ID, but | ||
175 | * just return constant nibble forever. This catches | ||
176 | * also those cases. */ | ||
177 | if (idlens[0] == 0 || idlens[0] > 0xFFF) { | ||
178 | printk (KERN_DEBUG "%s: reported broken Device ID" | ||
179 | " length of %#zX bytes\n", | ||
180 | port->name, idlens[0]); | ||
181 | return -EIO; | ||
182 | } | ||
183 | numidlens = 2; | ||
184 | } | ||
185 | |||
186 | /* Try to respect the given ID length despite all the bugs in | ||
187 | * the ID length. Read according to shortest possible ID | ||
188 | * first. */ | ||
189 | for (current_idlen = 0; current_idlen < numidlens; ++current_idlen) { | ||
190 | size_t idlen = idlens[current_idlen]; | ||
191 | if (idlen+1 >= count) | ||
192 | break; | ||
193 | |||
194 | retval = parport_read (port, buffer+len, idlen-len); | ||
195 | |||
196 | if (retval < 0) | ||
197 | return retval; | ||
198 | len += retval; | ||
199 | |||
200 | if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) { | ||
201 | if (belen != len) { | ||
202 | printk (KERN_DEBUG "%s: Device ID was %d bytes" | ||
203 | " while device told it would be %d" | ||
204 | " bytes\n", | ||
205 | port->name, len, belen); | ||
206 | } | ||
207 | goto done; | ||
208 | } | ||
209 | |||
210 | /* This might end reading the Device ID too | ||
211 | * soon. Hopefully the needed fields were already in | ||
212 | * the first 256 bytes or so that we must have read so | ||
213 | * far. */ | ||
214 | if (buffer[len-1] == ';') { | ||
215 | printk (KERN_DEBUG "%s: Device ID reading stopped" | ||
216 | " before device told data not available. " | ||
217 | "Current idlen %d of %d, len bytes %02X %02X\n", | ||
218 | port->name, current_idlen, numidlens, | ||
219 | length[0], length[1]); | ||
220 | goto done; | ||
221 | } | ||
222 | } | ||
223 | if (current_idlen < numidlens) { | ||
224 | /* Buffer not large enough, read to end of buffer. */ | ||
225 | size_t idlen, len2; | ||
226 | if (len+1 < count) { | ||
227 | retval = parport_read (port, buffer+len, count-len-1); | ||
228 | if (retval < 0) | ||
229 | return retval; | ||
230 | len += retval; | ||
231 | } | ||
232 | /* Read the whole ID since some devices would not | ||
233 | * otherwise give back the Device ID from beginning | ||
234 | * next time when asked. */ | ||
235 | idlen = idlens[current_idlen]; | ||
236 | len2 = len; | ||
237 | while(len2 < idlen && retval > 0) { | ||
238 | char tmp[4]; | ||
239 | retval = parport_read (port, tmp, | ||
240 | min(sizeof tmp, idlen-len2)); | ||
241 | if (retval < 0) | ||
242 | return retval; | ||
243 | len2 += retval; | ||
244 | } | ||
245 | } | ||
246 | /* In addition, there are broken devices out there that don't | ||
247 | even finish off with a semi-colon. We do not need to care | ||
248 | about those at this time. */ | ||
249 | done: | ||
250 | buffer[len] = '\0'; | ||
251 | return len; | ||
252 | } | ||
253 | |||
131 | /* Get Std 1284 Device ID. */ | 254 | /* Get Std 1284 Device ID. */ |
132 | ssize_t parport_device_id (int devnum, char *buffer, size_t len) | 255 | ssize_t parport_device_id (int devnum, char *buffer, size_t count) |
133 | { | 256 | { |
134 | ssize_t retval = -ENXIO; | 257 | ssize_t retval = -ENXIO; |
135 | struct pardevice *dev = parport_open (devnum, "Device ID probe", | 258 | struct pardevice *dev = parport_open (devnum, "Device ID probe", |
@@ -139,76 +262,20 @@ ssize_t parport_device_id (int devnum, char *buffer, size_t len) | |||
139 | 262 | ||
140 | parport_claim_or_block (dev); | 263 | parport_claim_or_block (dev); |
141 | 264 | ||
142 | /* Negotiate to compatibility mode, and then to device ID mode. | 265 | /* Negotiate to compatibility mode, and then to device ID |
143 | * (This is in case we are already in device ID mode.) */ | 266 | * mode. (This so that we start form beginning of device ID if |
267 | * already in device ID mode.) */ | ||
144 | parport_negotiate (dev->port, IEEE1284_MODE_COMPAT); | 268 | parport_negotiate (dev->port, IEEE1284_MODE_COMPAT); |
145 | retval = parport_negotiate (dev->port, | 269 | retval = parport_negotiate (dev->port, |
146 | IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID); | 270 | IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID); |
147 | 271 | ||
148 | if (!retval) { | 272 | if (!retval) { |
149 | int idlen; | 273 | retval = parport_read_device_id (dev->port, buffer, count); |
150 | unsigned char length[2]; | ||
151 | |||
152 | /* First two bytes are MSB,LSB of inclusive length. */ | ||
153 | retval = parport_read (dev->port, length, 2); | ||
154 | |||
155 | if (retval != 2) goto end_id; | ||
156 | |||
157 | idlen = (length[0] << 8) + length[1] - 2; | ||
158 | /* | ||
159 | * Check if the caller-allocated buffer is large enough | ||
160 | * otherwise bail out or there will be an at least off by one. | ||
161 | */ | ||
162 | if (idlen + 1 < len) | ||
163 | len = idlen; | ||
164 | else { | ||
165 | retval = -EINVAL; | ||
166 | goto out; | ||
167 | } | ||
168 | retval = parport_read (dev->port, buffer, len); | ||
169 | |||
170 | if (retval != len) | ||
171 | printk (KERN_DEBUG "%s: only read %Zd of %Zd ID bytes\n", | ||
172 | dev->port->name, retval, | ||
173 | len); | ||
174 | |||
175 | /* Some printer manufacturers mistakenly believe that | ||
176 | the length field is supposed to be _exclusive_. | ||
177 | In addition, there are broken devices out there | ||
178 | that don't even finish off with a semi-colon. */ | ||
179 | if (buffer[len - 1] != ';') { | ||
180 | ssize_t diff; | ||
181 | diff = parport_read (dev->port, buffer + len, 2); | ||
182 | retval += diff; | ||
183 | |||
184 | if (diff) | ||
185 | printk (KERN_DEBUG | ||
186 | "%s: device reported incorrect " | ||
187 | "length field (%d, should be %Zd)\n", | ||
188 | dev->port->name, idlen, retval); | ||
189 | else { | ||
190 | /* One semi-colon short of a device ID. */ | ||
191 | buffer[len++] = ';'; | ||
192 | printk (KERN_DEBUG "%s: faking semi-colon\n", | ||
193 | dev->port->name); | ||
194 | |||
195 | /* If we get here, I don't think we | ||
196 | need to worry about the possible | ||
197 | standard violation of having read | ||
198 | more than we were told to. The | ||
199 | device is non-compliant anyhow. */ | ||
200 | } | ||
201 | } | ||
202 | |||
203 | end_id: | ||
204 | buffer[len] = '\0'; | ||
205 | parport_negotiate (dev->port, IEEE1284_MODE_COMPAT); | 274 | parport_negotiate (dev->port, IEEE1284_MODE_COMPAT); |
275 | if (retval > 2) | ||
276 | parse_data (dev->port, dev->daisy, buffer+2); | ||
206 | } | 277 | } |
207 | 278 | ||
208 | if (retval > 2) | ||
209 | parse_data (dev->port, dev->daisy, buffer); | ||
210 | |||
211 | out: | ||
212 | parport_release (dev); | 279 | parport_release (dev); |
213 | parport_close (dev); | 280 | parport_close (dev); |
214 | return retval; | 281 | return retval; |