diff mbox series

[v2] ocfs2: add bounds checking to ocfs2_search_dirblock()

Message ID 20240528120140.15550-1-llfamsec@gmail.com (mailing list archive)
State New
Headers show
Series [v2] ocfs2: add bounds checking to ocfs2_search_dirblock() | expand

Commit Message

lei lu May 28, 2024, 12:01 p.m. UTC
Add a check to make sure all members of the ocfs2_dir_entry
don't stray beyond valid memory region.

Signed-off-by: lei lu <llfamsec@gmail.com>
---
 fs/ocfs2/dir.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

Comments

Joseph Qi May 29, 2024, 1:33 a.m. UTC | #1
On 5/28/24 8:01 PM, lei lu wrote:
> Add a check to make sure all members of the ocfs2_dir_entry
> don't stray beyond valid memory region.
> 
> Signed-off-by: lei lu <llfamsec@gmail.com>
> ---
>  fs/ocfs2/dir.c | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
> index d620d4c53c6f..56e2f8c62106 100644
> --- a/fs/ocfs2/dir.c
> +++ b/fs/ocfs2/dir.c
> @@ -352,19 +352,20 @@ static inline int ocfs2_search_dirblock(struct buffer_head *bh,
>  	de_buf = first_de;
>  	dlimit = de_buf + bytes;
>  
> -	while (de_buf < dlimit) {
> +	while (de_buf < dlimit - OCFS2_DIR_MEMBER_LEN) {
>  		/* this code is executed quadratically often */
>  		/* do minimal checking `by hand' */
>  
>  		de = (struct ocfs2_dir_entry *) de_buf;
>  
> -		if (de_buf + namelen <= dlimit &&
> +		/* do a full check */
> +		if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
> +			ret = -1;
> +			goto bail;
> +		}

What's the difference by moving full check above?
As I commented in v1, we may add a more check in ocfs2_check_dir_entry().

> +
> +		if (de->name + namelen <= dlimit &&

As discussed in v1, this change looks reasonable.

Thanks,
Joseph

>  		    ocfs2_match(namelen, name, de)) {
> -			/* found a match - just to be sure, do a full check */
> -			if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
> -				ret = -1;
> -				goto bail;
> -			}
>  			*res_dir = de;
>  			ret = 1;
>  			goto bail;
lei lu May 29, 2024, 3:18 a.m. UTC | #2
Joseph Qi <joseph.qi@linux.alibaba.com> 于2024年5月29日周三 09:33写道:
>
>
>
> On 5/28/24 8:01 PM, lei lu wrote:
> > Add a check to make sure all members of the ocfs2_dir_entry
> > don't stray beyond valid memory region.
> >
> > Signed-off-by: lei lu <llfamsec@gmail.com>
> > ---
> >  fs/ocfs2/dir.c | 15 ++++++++-------
> >  1 file changed, 8 insertions(+), 7 deletions(-)
> >
> > diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
> > index d620d4c53c6f..56e2f8c62106 100644
> > --- a/fs/ocfs2/dir.c
> > +++ b/fs/ocfs2/dir.c
> > @@ -352,19 +352,20 @@ static inline int ocfs2_search_dirblock(struct buffer_head *bh,
> >       de_buf = first_de;
> >       dlimit = de_buf + bytes;
> >
> > -     while (de_buf < dlimit) {
> > +     while (de_buf < dlimit - OCFS2_DIR_MEMBER_LEN) {
> >               /* this code is executed quadratically often */
> >               /* do minimal checking `by hand' */
> >
> >               de = (struct ocfs2_dir_entry *) de_buf;
> >
> > -             if (de_buf + namelen <= dlimit &&
> > +             /* do a full check */
> > +             if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
> > +                     ret = -1;
> > +                     goto bail;
> > +             }
>
> What's the difference by moving full check above?
> As I commented in v1, we may add a more check in ocfs2_check_dir_entry().

Thanks for your time.

If no match is found, there is no check for de. I'm not sure if there
will be any OOB elsewhere when "de->rec_len <
OCFS2_DIR_REC_LEN(de->name_len)".

I'm not sure what "(de - bh->b_data) + de_len > bytes -
OCFS2_DIR_REC_LEN(de->name_len)" means in V1, bh->b_data is the start
offset of ocfs2_dinode. bytes is just the size of the de list. Maybe
we should check "((char *) de - bh->b_data) + rlen > sizeof(struct
ocfs2_dinode) + bytes" which is same as "((char *) de - bh->b_data) +
rlen > dlimit".

>
> > +
> > +             if (de->name + namelen <= dlimit &&
>
> As discussed in v1, this change looks reasonable.
>
> Thanks,
> Joseph
>
> >                   ocfs2_match(namelen, name, de)) {
> > -                     /* found a match - just to be sure, do a full check */
> > -                     if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
> > -                             ret = -1;
> > -                             goto bail;
> > -                     }
> >                       *res_dir = de;
> >                       ret = 1;
> >                       goto bail;
Joseph Qi May 29, 2024, 6:15 a.m. UTC | #3
On 5/29/24 11:18 AM, lei lu wrote:
> Joseph Qi <joseph.qi@linux.alibaba.com> 于2024年5月29日周三 09:33写道:
>>
>>
>>
>> On 5/28/24 8:01 PM, lei lu wrote:
>>> Add a check to make sure all members of the ocfs2_dir_entry
>>> don't stray beyond valid memory region.
>>>
>>> Signed-off-by: lei lu <llfamsec@gmail.com>
>>> ---
>>>  fs/ocfs2/dir.c | 15 ++++++++-------
>>>  1 file changed, 8 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
>>> index d620d4c53c6f..56e2f8c62106 100644
>>> --- a/fs/ocfs2/dir.c
>>> +++ b/fs/ocfs2/dir.c
>>> @@ -352,19 +352,20 @@ static inline int ocfs2_search_dirblock(struct buffer_head *bh,
>>>       de_buf = first_de;
>>>       dlimit = de_buf + bytes;
>>>
>>> -     while (de_buf < dlimit) {
>>> +     while (de_buf < dlimit - OCFS2_DIR_MEMBER_LEN) {
>>>               /* this code is executed quadratically often */
>>>               /* do minimal checking `by hand' */
>>>
>>>               de = (struct ocfs2_dir_entry *) de_buf;
>>>
>>> -             if (de_buf + namelen <= dlimit &&
>>> +             /* do a full check */
>>> +             if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
>>> +                     ret = -1;
>>> +                     goto bail;
>>> +             }
>>
>> What's the difference by moving full check above?
>> As I commented in v1, we may add a more check in ocfs2_check_dir_entry().
> 
> Thanks for your time.
> 
> If no match is found, there is no check for de. I'm not sure if there
> will be any OOB elsewhere when "de->rec_len <
> OCFS2_DIR_REC_LEN(de->name_len)".
> 
> I'm not sure what "(de - bh->b_data) + de_len > bytes -
> OCFS2_DIR_REC_LEN(de->name_len)" means in V1, bh->b_data is the start
> offset of ocfs2_dinode. bytes is just the size of the de list. Maybe
> we should check "((char *) de - bh->b_data) + rlen > sizeof(struct
> ocfs2_dinode) + bytes" which is same as "((char *) de - bh->b_data) +
> rlen > dlimit".
> 

You can refer ext4_check_dir_entry() for details.

>>
>>> +
>>> +             if (de->name + namelen <= dlimit &&
>>
>> As discussed in v1, this change looks reasonable.
>>
>> Thanks,
>> Joseph
>>
>>>                   ocfs2_match(namelen, name, de)) {
>>> -                     /* found a match - just to be sure, do a full check */
>>> -                     if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
>>> -                             ret = -1;
>>> -                             goto bail;
>>> -                     }
>>>                       *res_dir = de;
>>>                       ret = 1;
>>>                       goto bail;
lei lu May 29, 2024, 6:58 a.m. UTC | #4
Thanks, I apologize for not being very familiar.
Could you take some time to write a patch for this issue.

Thanks,
LL

Joseph Qi <joseph.qi@linux.alibaba.com> 于2024年5月29日周三 14:15写道:
>
>
>
> On 5/29/24 11:18 AM, lei lu wrote:
> > Joseph Qi <joseph.qi@linux.alibaba.com> 于2024年5月29日周三 09:33写道:
> >>
> >>
> >>
> >> On 5/28/24 8:01 PM, lei lu wrote:
> >>> Add a check to make sure all members of the ocfs2_dir_entry
> >>> don't stray beyond valid memory region.
> >>>
> >>> Signed-off-by: lei lu <llfamsec@gmail.com>
> >>> ---
> >>>  fs/ocfs2/dir.c | 15 ++++++++-------
> >>>  1 file changed, 8 insertions(+), 7 deletions(-)
> >>>
> >>> diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
> >>> index d620d4c53c6f..56e2f8c62106 100644
> >>> --- a/fs/ocfs2/dir.c
> >>> +++ b/fs/ocfs2/dir.c
> >>> @@ -352,19 +352,20 @@ static inline int ocfs2_search_dirblock(struct buffer_head *bh,
> >>>       de_buf = first_de;
> >>>       dlimit = de_buf + bytes;
> >>>
> >>> -     while (de_buf < dlimit) {
> >>> +     while (de_buf < dlimit - OCFS2_DIR_MEMBER_LEN) {
> >>>               /* this code is executed quadratically often */
> >>>               /* do minimal checking `by hand' */
> >>>
> >>>               de = (struct ocfs2_dir_entry *) de_buf;
> >>>
> >>> -             if (de_buf + namelen <= dlimit &&
> >>> +             /* do a full check */
> >>> +             if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
> >>> +                     ret = -1;
> >>> +                     goto bail;
> >>> +             }
> >>
> >> What's the difference by moving full check above?
> >> As I commented in v1, we may add a more check in ocfs2_check_dir_entry().
> >
> > Thanks for your time.
> >
> > If no match is found, there is no check for de. I'm not sure if there
> > will be any OOB elsewhere when "de->rec_len <
> > OCFS2_DIR_REC_LEN(de->name_len)".
> >
> > I'm not sure what "(de - bh->b_data) + de_len > bytes -
> > OCFS2_DIR_REC_LEN(de->name_len)" means in V1, bh->b_data is the start
> > offset of ocfs2_dinode. bytes is just the size of the de list. Maybe
> > we should check "((char *) de - bh->b_data) + rlen > sizeof(struct
> > ocfs2_dinode) + bytes" which is same as "((char *) de - bh->b_data) +
> > rlen > dlimit".
> >
>
> You can refer ext4_check_dir_entry() for details.
>
> >>
> >>> +
> >>> +             if (de->name + namelen <= dlimit &&
> >>
> >> As discussed in v1, this change looks reasonable.
> >>
> >> Thanks,
> >> Joseph
> >>
> >>>                   ocfs2_match(namelen, name, de)) {
> >>> -                     /* found a match - just to be sure, do a full check */
> >>> -                     if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
> >>> -                             ret = -1;
> >>> -                             goto bail;
> >>> -                     }
> >>>                       *res_dir = de;
> >>>                       ret = 1;
> >>>                       goto bail;
diff mbox series

Patch

diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index d620d4c53c6f..56e2f8c62106 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -352,19 +352,20 @@  static inline int ocfs2_search_dirblock(struct buffer_head *bh,
 	de_buf = first_de;
 	dlimit = de_buf + bytes;
 
-	while (de_buf < dlimit) {
+	while (de_buf < dlimit - OCFS2_DIR_MEMBER_LEN) {
 		/* this code is executed quadratically often */
 		/* do minimal checking `by hand' */
 
 		de = (struct ocfs2_dir_entry *) de_buf;
 
-		if (de_buf + namelen <= dlimit &&
+		/* do a full check */
+		if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
+			ret = -1;
+			goto bail;
+		}
+
+		if (de->name + namelen <= dlimit &&
 		    ocfs2_match(namelen, name, de)) {
-			/* found a match - just to be sure, do a full check */
-			if (!ocfs2_check_dir_entry(dir, de, bh, offset)) {
-				ret = -1;
-				goto bail;
-			}
 			*res_dir = de;
 			ret = 1;
 			goto bail;