Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
afc11cbc
Commit
afc11cbc
authored
May 28, 2004
by
Alexander Viro
Committed by
Linus Torvalds
May 28, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] sparse: ide-proc.c fixes
Fixed dereferencing userland pointers, general idiocy in parsing.
parent
70c7987d
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
224 additions
and
213 deletions
+224
-213
drivers/ide/ide-proc.c
drivers/ide/ide-proc.c
+224
-213
No files found.
drivers/ide/ide-proc.c
View file @
afc11cbc
...
...
@@ -74,207 +74,210 @@
#include <asm/io.h>
static
int
ide_getxdigit
(
char
c
)
{
int
digit
;
if
(
isdigit
(
c
))
digit
=
c
-
'0'
;
else
if
(
isxdigit
(
c
))
digit
=
tolower
(
c
)
-
'a'
+
10
;
else
digit
=
-
1
;
return
digit
;
}
static
int
xx_xx_parse_error
(
const
char
*
data
,
unsigned
long
len
,
const
char
*
msg
)
{
char
errbuf
[
16
];
int
i
;
if
(
len
>=
sizeof
(
errbuf
))
len
=
sizeof
(
errbuf
)
-
1
;
for
(
i
=
0
;
i
<
len
;
++
i
)
{
char
c
=
data
[
i
];
if
(
!
c
||
c
==
'\n'
)
c
=
'\0'
;
else
if
(
iscntrl
(
c
))
c
=
'?'
;
errbuf
[
i
]
=
c
;
}
errbuf
[
i
]
=
'\0'
;
printk
(
"proc_ide: error: %s: '%s'
\n
"
,
msg
,
errbuf
);
return
-
EINVAL
;
}
static
int
proc_ide_write_config
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
static
int
proc_ide_write_config
(
struct
file
*
file
,
const
char
__user
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
ide_hwif_t
*
hwif
=
(
ide_hwif_t
*
)
data
;
int
for_real
=
0
;
unsigned
long
startn
=
0
,
n
,
flags
;
const
char
*
start
=
NULL
,
*
msg
=
NULL
;
ide_hwgroup_t
*
mygroup
=
(
ide_hwgroup_t
*
)(
hwif
->
hwgroup
);
ide_hwgroup_t
*
mategroup
=
NULL
;
unsigned
long
timeout
;
unsigned
long
flags
;
const
char
*
start
=
NULL
,
*
msg
=
NULL
;
struct
entry
{
u32
val
;
u16
reg
;
u8
size
;
u8
pci
;
}
*
prog
,
*
q
,
*
r
;
int
want_pci
=
0
;
char
*
buf
,
*
s
;
int
err
;
if
(
hwif
->
mate
&&
hwif
->
mate
->
hwgroup
)
mategroup
=
(
ide_hwgroup_t
*
)(
hwif
->
mate
->
hwgroup
);
if
(
!
capable
(
CAP_SYS_ADMIN
)
||
!
capable
(
CAP_SYS_RAWIO
))
return
-
EACCES
;
/*
* Skip over leading whitespace
*/
while
(
count
&&
isspace
(
*
buffer
))
{
--
count
;
++
buffer
;
if
(
count
>=
PAGE_SIZE
)
return
-
EINVAL
;
s
=
buf
=
(
char
*
)
__get_free_page
(
GFP_USER
);
if
(
!
buf
)
return
-
ENOMEM
;
err
=
-
ENOMEM
;
q
=
prog
=
(
struct
entry
*
)
__get_free_page
(
GFP_USER
);
if
(
!
prog
)
goto
out
;
err
=
-
EFAULT
;
if
(
copy_from_user
(
buf
,
buffer
,
count
))
goto
out1
;
buf
[
count
]
=
'\0'
;
while
(
isspace
(
*
s
))
s
++
;
while
(
*
s
)
{
char
*
p
;
int
digits
;
start
=
s
;
if
((
char
*
)(
q
+
1
)
>
(
char
*
)
prog
+
PAGE_SIZE
)
{
msg
=
"too many entries"
;
goto
parse_error
;
}
switch
(
*
s
++
)
{
case
'R'
:
q
->
pci
=
0
;
break
;
case
'P'
:
q
->
pci
=
1
;
want_pci
=
1
;
break
;
default:
msg
=
"expected 'R' or 'P'"
;
goto
parse_error
;
}
q
->
reg
=
simple_strtoul
(
s
,
&
p
,
16
);
digits
=
p
-
s
;
if
(
!
digits
||
digits
>
4
||
(
q
->
pci
&&
q
->
reg
>
0xff
))
{
msg
=
"bad/missing register number"
;
goto
parse_error
;
}
if
(
*
p
++
!=
':'
)
{
msg
=
"missing ':'"
;
goto
parse_error
;
}
q
->
val
=
simple_strtoul
(
p
,
&
s
,
16
);
digits
=
s
-
p
;
if
(
digits
!=
2
&&
digits
!=
4
&&
digits
!=
8
)
{
msg
=
"bad data, 2/4/8 digits required"
;
goto
parse_error
;
}
q
->
size
=
digits
/
2
;
if
(
q
->
pci
)
{
#ifdef CONFIG_BLK_DEV_IDEPCI
if
(
q
->
reg
&
(
q
->
size
-
1
))
{
msg
=
"misaligned access"
;
goto
parse_error
;
}
#else
msg
=
"not a PCI device"
;
goto
parse_error
;
#endif
/* CONFIG_BLK_DEV_IDEPCI */
}
q
++
;
if
(
*
s
&&
!
isspace
(
*
s
++
))
{
msg
=
"expected whitespace after data"
;
goto
parse_error
;
}
while
(
isspace
(
*
s
))
s
++
;
}
/*
* Do one full pass to verify all parameters,
* then do another to actually write the regs.
* What follows below is fucking insane, even for IDE people.
* For now I've dealt with the obvious problems on the parsing
* side, but IMNSHO we should simply remove the write access
* to /proc/ide/.../config, killing that FPOS completely.
*/
err
=
-
EBUSY
;
timeout
=
jiffies
+
(
3
*
HZ
);
spin_lock_irqsave
(
&
ide_lock
,
flags
);
do
{
const
char
*
p
;
if
(
for_real
)
{
unsigned
long
timeout
=
jiffies
+
(
3
*
HZ
);
ide_hwgroup_t
*
mygroup
=
(
ide_hwgroup_t
*
)(
hwif
->
hwgroup
);
ide_hwgroup_t
*
mategroup
=
NULL
;
if
(
hwif
->
mate
&&
hwif
->
mate
->
hwgroup
)
mategroup
=
(
ide_hwgroup_t
*
)(
hwif
->
mate
->
hwgroup
);
spin_lock_irqsave
(
&
ide_lock
,
flags
);
while
(
mygroup
->
busy
||
(
mategroup
&&
mategroup
->
busy
))
{
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
if
(
time_after
(
jiffies
,
timeout
))
{
printk
(
"/proc/ide/%s/config: channel(s) busy, cannot write
\n
"
,
hwif
->
name
);
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
return
-
EBUSY
;
}
spin_lock_irqsave
(
&
ide_lock
,
flags
);
}
while
(
mygroup
->
busy
||
(
mategroup
&&
mategroup
->
busy
))
{
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
if
(
time_after
(
jiffies
,
timeout
))
{
printk
(
"/proc/ide/%s/config: channel(s) busy, cannot write
\n
"
,
hwif
->
name
);
goto
out1
;
}
p
=
buffer
;
n
=
count
;
while
(
n
>
0
)
{
int
d
,
digits
;
unsigned
int
reg
=
0
,
val
=
0
,
is_pci
;
start
=
p
;
startn
=
n
--
;
switch
(
*
p
++
)
{
case
'R'
:
is_pci
=
0
;
break
;
case
'P'
:
is_pci
=
1
;
spin_lock_irqsave
(
&
ide_lock
,
flags
);
}
#ifdef CONFIG_BLK_DEV_IDEPCI
if
(
hwif
->
pci_dev
&&
!
hwif
->
pci_dev
->
vendor
)
break
;
if
(
want_pci
&&
(
!
hwif
->
pci_dev
||
hwif
->
pci_dev
->
vendor
))
{
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
printk
(
"proc_ide: PCI registers not accessible for %s
\n
"
,
hwif
->
name
);
err
=
-
EINVAL
;
goto
out1
;
}
#endif
/* CONFIG_BLK_DEV_IDEPCI */
msg
=
"not a PCI device"
;
goto
parse_error
;
default:
msg
=
"expected 'R' or 'P'"
;
goto
parse_error
;
}
digits
=
0
;
while
(
n
>
0
&&
(
d
=
ide_getxdigit
(
*
p
))
>=
0
)
{
reg
=
(
reg
<<
4
)
|
d
;
--
n
;
++
p
;
++
digits
;
}
if
(
!
digits
||
(
digits
>
4
)
||
(
is_pci
&&
reg
>
0xff
))
{
msg
=
"bad/missing register number"
;
goto
parse_error
;
}
if
(
n
--
==
0
||
*
p
++
!=
':'
)
{
msg
=
"missing ':'"
;
goto
parse_error
;
}
digits
=
0
;
while
(
n
>
0
&&
(
d
=
ide_getxdigit
(
*
p
))
>=
0
)
{
val
=
(
val
<<
4
)
|
d
;
--
n
;
++
p
;
++
digits
;
}
if
(
digits
!=
2
&&
digits
!=
4
&&
digits
!=
8
)
{
msg
=
"bad data, 2/4/8 digits required"
;
goto
parse_error
;
}
if
(
n
>
0
&&
!
isspace
(
*
p
))
{
msg
=
"expected whitespace after data"
;
goto
parse_error
;
}
while
(
n
>
0
&&
isspace
(
*
p
))
{
--
n
;
++
p
;
}
for
(
r
=
prog
;
r
<
q
;
r
++
)
{
unsigned
int
reg
=
r
->
reg
,
val
=
r
->
val
;
if
(
r
->
pci
)
{
#ifdef CONFIG_BLK_DEV_IDEPCI
if
(
is_pci
&&
(
reg
&
((
digits
>>
1
)
-
1
)))
{
msg
=
"misaligned access"
;
goto
parse_error
;
int
rc
=
0
;
struct
pci_dev
*
dev
=
hwif
->
pci_dev
;
switch
(
q
->
size
)
{
case
1
:
msg
=
"byte"
;
rc
=
pci_write_config_byte
(
dev
,
reg
,
val
);
break
;
case
2
:
msg
=
"word"
;
rc
=
pci_write_config_word
(
dev
,
reg
,
val
);
break
;
case
4
:
msg
=
"dword"
;
rc
=
pci_write_config_dword
(
dev
,
reg
,
val
);
break
;
}
if
(
rc
)
{
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
printk
(
"proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x
\n
"
,
msg
,
dev
->
bus
->
number
,
dev
->
devfn
,
reg
,
val
);
printk
(
"proc_ide_write_config: error %d
\n
"
,
rc
);
err
=
-
EIO
;
goto
out1
;
}
#endif
/* CONFIG_BLK_DEV_IDEPCI */
if
(
for_real
)
{
#if 0
printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits);
#endif
if
(
is_pci
)
{
#ifdef CONFIG_BLK_DEV_IDEPCI
int
rc
=
0
;
struct
pci_dev
*
dev
=
hwif
->
pci_dev
;
switch
(
digits
)
{
case
2
:
msg
=
"byte"
;
rc
=
pci_write_config_byte
(
dev
,
reg
,
val
);
break
;
case
4
:
msg
=
"word"
;
rc
=
pci_write_config_word
(
dev
,
reg
,
val
);
break
;
case
8
:
msg
=
"dword"
;
rc
=
pci_write_config_dword
(
dev
,
reg
,
val
);
break
;
}
if
(
rc
)
{
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
printk
(
"proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x
\n
"
,
msg
,
dev
->
bus
->
number
,
dev
->
devfn
,
reg
,
val
);
printk
(
"proc_ide_write_config: error %d
\n
"
,
rc
);
return
-
EIO
;
}
#endif
/* CONFIG_BLK_DEV_IDEPCI */
}
else
{
/* not pci */
}
else
{
/* not pci */
#if !defined(__mc68000__) && !defined(CONFIG_APUS)
/*
* Geert Uytterhoeven
*
* unless you can explain me what it really does.
* On m68k, we don't have outw() and outl() yet,
* and I need a good reason to implement it.
*
* BTW, IMHO the main remaining portability problem with the IDE driver
* is that it mixes IO (ioport) and MMIO (iomem) access on different platforms.
*
* I think all accesses should be done using
*
* ide_in[bwl](ide_device_instance, offset)
* ide_out[bwl](ide_device_instance, value, offset)
*
* so the architecture specific code can #define ide_{in,out}[bwl] to the
* appropriate function.
*
*/
switch
(
digits
)
{
case
2
:
hwif
->
OUTB
(
val
,
reg
);
break
;
case
4
:
hwif
->
OUTW
(
val
,
reg
);
break
;
case
8
:
hwif
->
OUTL
(
val
,
reg
);
break
;
}
#endif
/* !__mc68000__ && !CONFIG_APUS */
}
* Geert Uytterhoeven
*
* unless you can explain me what it really does.
* On m68k, we don't have outw() and outl() yet,
* and I need a good reason to implement it.
*
* BTW, IMHO the main remaining portability problem with the IDE driver
* is that it mixes IO (ioport) and MMIO (iomem) access on different platforms.
*
* I think all accesses should be done using
*
* ide_in[bwl](ide_device_instance, offset)
* ide_out[bwl](ide_device_instance, value, offset)
*
* so the architecture specific code can #define ide_{in,out}[bwl] to the
* appropriate function.
*
*/
switch
(
r
->
size
)
{
case
1
:
hwif
->
OUTB
(
val
,
reg
);
break
;
case
2
:
hwif
->
OUTW
(
val
,
reg
);
break
;
case
4
:
hwif
->
OUTL
(
val
,
reg
);
break
;
}
#endif
/* !__mc68000__ && !CONFIG_APUS */
}
}
while
(
!
for_real
++
);
}
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
return
count
;
err
=
count
;
out1:
free_page
((
unsigned
long
)
prog
);
out:
free_page
((
unsigned
long
)
buf
);
return
err
;
parse_error:
spin_unlock_irqrestore
(
&
ide_lock
,
flags
);
printk
(
"parse error
\n
"
);
return
xx_xx_parse_error
(
start
,
startn
,
msg
);
printk
(
"proc_ide: error: %s: '%s'
\n
"
,
msg
,
start
);
err
=
-
EINVAL
;
goto
out1
;
}
int
proc_ide_read_config
...
...
@@ -316,16 +319,6 @@ int proc_ide_read_config
EXPORT_SYMBOL
(
proc_ide_read_config
);
static
int
ide_getdigit
(
char
c
)
{
int
digit
;
if
(
isdigit
(
c
))
digit
=
c
-
'0'
;
else
digit
=
-
1
;
return
digit
;
}
static
int
proc_ide_read_imodel
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
...
...
@@ -470,37 +463,50 @@ EXPORT_SYMBOL(proc_ide_read_settings);
#define MAX_LEN 30
int
proc_ide_write_settings
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
int
proc_ide_write_settings
(
struct
file
*
file
,
const
char
__user
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
ide_drive_t
*
drive
=
(
ide_drive_t
*
)
data
;
char
name
[
MAX_LEN
+
1
];
int
for_real
=
0
,
len
;
int
for_real
=
0
;
unsigned
long
n
;
const
char
*
start
=
NULL
;
ide_settings_t
*
setting
;
char
*
buf
,
*
s
;
if
(
!
capable
(
CAP_SYS_ADMIN
))
return
-
EACCES
;
if
(
count
>=
PAGE_SIZE
)
return
-
EINVAL
;
s
=
buf
=
(
char
*
)
__get_free_page
(
GFP_USER
);
if
(
!
buf
)
return
-
ENOMEM
;
if
(
copy_from_user
(
buf
,
buffer
,
count
))
{
free_page
((
unsigned
long
)
buf
);
return
-
EFAULT
;
}
buf
[
count
]
=
'\0'
;
/*
* Skip over leading whitespace
*/
while
(
count
&&
isspace
(
*
buffer
))
{
while
(
count
&&
isspace
(
*
s
))
{
--
count
;
++
buffer
;
++
s
;
}
/*
* Do one full pass to verify all parameters,
* then do another to actually write the new settings.
*/
do
{
const
char
*
p
;
p
=
buffer
;
char
*
p
=
s
;
n
=
count
;
while
(
n
>
0
)
{
int
d
,
digits
;
unsigned
int
val
=
0
;
start
=
p
;
unsigned
val
;
char
*
q
=
p
;
while
(
n
>
0
&&
*
p
!=
':'
)
{
--
n
;
...
...
@@ -508,30 +514,27 @@ int proc_ide_write_settings
}
if
(
*
p
!=
':'
)
goto
parse_error
;
len
=
min_t
(
int
,
p
-
start
,
MAX_LEN
);
strncpy
(
name
,
start
,
min
(
len
,
MAX_LEN
));
name
[
len
]
=
0
;
if
(
p
-
q
>
MAX_LEN
)
goto
parse_error
;
memcpy
(
name
,
q
,
p
-
q
);
name
[
p
-
q
]
=
0
;
if
(
n
>
0
)
{
--
n
;
p
++
;
}
else
goto
parse_error
;
digits
=
0
;
while
(
n
>
0
&&
(
d
=
ide_getdigit
(
*
p
))
>=
0
)
{
val
=
(
val
*
10
)
+
d
;
--
n
;
++
p
;
++
digits
;
}
val
=
simple_strtoul
(
p
,
&
q
,
10
);
n
-=
q
-
p
;
p
=
q
;
if
(
n
>
0
&&
!
isspace
(
*
p
))
goto
parse_error
;
while
(
n
>
0
&&
isspace
(
*
p
))
{
--
n
;
++
p
;
}
down
(
&
ide_setting_sem
);
setting
=
ide_find_setting_by_name
(
drive
,
name
);
if
(
!
setting
)
...
...
@@ -544,8 +547,10 @@ int proc_ide_write_settings
up
(
&
ide_setting_sem
);
}
}
while
(
!
for_real
++
);
free_page
((
unsigned
long
)
buf
);
return
count
;
parse_error:
free_page
((
unsigned
long
)
buf
);
printk
(
"proc_ide_write_settings(): parse error
\n
"
);
return
-
EINVAL
;
}
...
...
@@ -612,13 +617,19 @@ int proc_ide_read_driver
EXPORT_SYMBOL
(
proc_ide_read_driver
);
int
proc_ide_write_driver
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
(
struct
file
*
file
,
const
char
__user
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
ide_drive_t
*
drive
=
(
ide_drive_t
*
)
data
;
char
name
[
32
];
if
(
!
capable
(
CAP_SYS_ADMIN
))
return
-
EACCES
;
if
(
ide_replace_subdriver
(
drive
,
buffer
))
if
(
count
>
31
)
count
=
31
;
if
(
copy_from_user
(
name
,
buffer
,
count
))
return
-
EFAULT
;
name
[
count
]
=
'\0'
;
if
(
ide_replace_subdriver
(
drive
,
name
))
return
-
EINVAL
;
return
count
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment