* [PATCH] Typecasting required for comparing unlike datatypes @ 2010-12-08 12:55 Harsh Prateek Bora 2010-12-09 18:32 ` Harsh Bora ` (2 more replies) 0 siblings, 3 replies; 15+ messages in thread From: Harsh Prateek Bora @ 2010-12-08 12:55 UTC (permalink / raw) To: linux-fsdevel, fengguang.wu, kamezawa.hiroyu Cc: aneesh.kumar, jvrao, Harsh Prateek Bora The existing code causes the if condition to pass when it should fail on a *64-bit kernel* because of implicit data type conversions. It can be observed by passing pos = -1 and count = some positive number. This results in function returning EOVERFLOW instead of EINVAL. With this patch, the function returns EINVAL when pos is -1 and count is a positive number. This can be tested by calling sendfile with offset = -1 and count = some positive number on a 64-bit kernel. Signed-off-by: Harsh Prateek Bora <harsh@linux.vnet.ibm.com> --- fs/read_write.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index 431a0ed..a8eabd4 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -38,7 +38,7 @@ __negative_fpos_check(struct file *file, loff_t pos, size_t count) * pos or pos+count is negative here, check overflow. * too big "count" will be caught in rw_verify_area(). */ - if ((pos < 0) && (pos + count < pos)) + if ((pos < 0) && ( (loff_t) (pos + count) < pos)) return -EOVERFLOW; if (file->f_mode & FMODE_UNSIGNED_OFFSET) return 0; -- 1.7.1.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-08 12:55 [PATCH] Typecasting required for comparing unlike datatypes Harsh Prateek Bora @ 2010-12-09 18:32 ` Harsh Bora 2010-12-10 0:06 ` KAMEZAWA Hiroyuki 2010-12-10 0:53 ` KAMEZAWA Hiroyuki 2010-12-10 8:18 ` KAMEZAWA Hiroyuki 2 siblings, 1 reply; 15+ messages in thread From: Harsh Bora @ 2010-12-09 18:32 UTC (permalink / raw) To: viro Cc: Harsh Prateek Bora, linux-fsdevel, fengguang.wu, kamezawa.hiroyu, aneesh.kumar, jvrao Hi Al, Any comments? On 12/08/2010 06:25 PM, Harsh Prateek Bora wrote: > The existing code causes the if condition to pass when it should fail > on a *64-bit kernel* because of implicit data type conversions. It can > be observed by passing pos = -1 and count = some positive number. > This results in function returning EOVERFLOW instead of EINVAL. > > With this patch, the function returns EINVAL when pos is -1 and count > is a positive number. This can be tested by calling sendfile with > offset = -1 and count = some positive number on a 64-bit kernel. > > Signed-off-by: Harsh Prateek Bora<harsh@linux.vnet.ibm.com> > --- > fs/read_write.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/fs/read_write.c b/fs/read_write.c > index 431a0ed..a8eabd4 100644 > --- a/fs/read_write.c > +++ b/fs/read_write.c > @@ -38,7 +38,7 @@ __negative_fpos_check(struct file *file, loff_t pos, size_t count) > * pos or pos+count is negative here, check overflow. > * too big "count" will be caught in rw_verify_area(). > */ > - if ((pos< 0)&& (pos + count< pos)) > + if ((pos< 0)&& ( (loff_t) (pos + count)< pos)) > return -EOVERFLOW; > if (file->f_mode& FMODE_UNSIGNED_OFFSET) > return 0; ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-09 18:32 ` Harsh Bora @ 2010-12-10 0:06 ` KAMEZAWA Hiroyuki 0 siblings, 0 replies; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-10 0:06 UTC (permalink / raw) To: Harsh Bora; +Cc: viro, linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Fri, 10 Dec 2010 00:02:03 +0530 Harsh Bora <harsh@linux.vnet.ibm.com> wrote: > Hi Al, > Any comments? > > On 12/08/2010 06:25 PM, Harsh Prateek Bora wrote: > > The existing code causes the if condition to pass when it should fail > > on a *64-bit kernel* because of implicit data type conversions. It can > > be observed by passing pos = -1 and count = some positive number. > > This results in function returning EOVERFLOW instead of EINVAL. > > > > With this patch, the function returns EINVAL when pos is -1 and count > > is a positive number. This can be tested by calling sendfile with > > offset = -1 and count = some positive number on a 64-bit kernel. > > > > Signed-off-by: Harsh Prateek Bora<harsh@linux.vnet.ibm.com> > > --- > > fs/read_write.c | 2 +- > > 1 files changed, 1 insertions(+), 1 deletions(-) > > > > diff --git a/fs/read_write.c b/fs/read_write.c > > index 431a0ed..a8eabd4 100644 > > --- a/fs/read_write.c > > +++ b/fs/read_write.c > > @@ -38,7 +38,7 @@ __negative_fpos_check(struct file *file, loff_t pos, size_t count) > > * pos or pos+count is negative here, check overflow. > > * too big "count" will be caught in rw_verify_area(). > > */ > > - if ((pos< 0)&& (pos + count< pos)) > > + if ((pos< 0)&& ( (loff_t) (pos + count)< pos)) > > return -EOVERFLOW; Hmm, maybe if (!(file->f_mode & FMODE_UNSIGNED_OFFSET)) return -EINVAL; if ((pos < 0) && (pos + count < pos)) return -EOVERFLOW; return 0; is a correct order ? Thanks, -Kame ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-08 12:55 [PATCH] Typecasting required for comparing unlike datatypes Harsh Prateek Bora 2010-12-09 18:32 ` Harsh Bora @ 2010-12-10 0:53 ` KAMEZAWA Hiroyuki 2010-12-10 6:39 ` Harsh Bora 2010-12-10 8:18 ` KAMEZAWA Hiroyuki 2 siblings, 1 reply; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-10 0:53 UTC (permalink / raw) To: Harsh Prateek Bora; +Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Wed, 8 Dec 2010 18:25:00 +0530 Harsh Prateek Bora <harsh@linux.vnet.ibm.com> wrote: > The existing code causes the if condition to pass when it should fail > on a *64-bit kernel* because of implicit data type conversions. It can > be observed by passing pos = -1 and count = some positive number. > This results in function returning EOVERFLOW instead of EINVAL. > > With this patch, the function returns EINVAL when pos is -1 and count > is a positive number. This can be tested by calling sendfile with > offset = -1 and count = some positive number on a 64-bit kernel. > Hmm, is this clearer ? == commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for negative (unsigned) page offset for very large files as /proc/<pid>/mem and /dev/mem. In that patch, overlap check routine is added but it was wrong. Considering 'pos' is loff_t, a signed value, In usual case, at comparing 'pos' and 'pos+count' (positive) / (positive) OK (positive) / (nevative) EOVERFLOW (negative) / (positive) EINVAL (negative) / (negative) EINVAL In FMODE_UNSIGNED_OFFSET case, (positive) / (positive) OK (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) (nevative) / (negative) OK (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> --- fs/read_write.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) Index: linux-2.6.37-rc5/fs/read_write.c =================================================================== --- linux-2.6.37-rc5.orig/fs/read_write.c +++ linux-2.6.37-rc5/fs/read_write.c @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, * pos or pos+count is negative here, check overflow. * too big "count" will be caught in rw_verify_area(). */ - if ((pos < 0) && (pos + count < pos)) + /* negative pos is allowed only when the flag is set */ + if (!(file->f_mode & FMODE_UNSIGNED_OFFSET)) { + if ((pos > 0) && (pos + count > 0)) + return 0; + if ((pos > 0) && (pos + count < 0)) + return -EOVERFLOW; + return -EINVAL; + } + /* + * The file supports 'unsigned long' offset. (but loff_t is signed) + * When pos is negative, -1 is the biggest number. So if pos + count + * is larger than pos, it's overflow. + * (ex) -1 + 10 = 9 ...means + * 0xffff + 0xa = 0x9 => overflow. + */ + if ((pos < 0) && (pos + count > 0)) return -EOVERFLOW; - if (file->f_mode & FMODE_UNSIGNED_OFFSET) - return 0; - return -EINVAL; + return 0; } /** ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 0:53 ` KAMEZAWA Hiroyuki @ 2010-12-10 6:39 ` Harsh Bora 2010-12-10 7:01 ` KAMEZAWA Hiroyuki 0 siblings, 1 reply; 15+ messages in thread From: Harsh Bora @ 2010-12-10 6:39 UTC (permalink / raw) To: KAMEZAWA Hiroyuki; +Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On 12/10/2010 06:23 AM, KAMEZAWA Hiroyuki wrote: > On Wed, 8 Dec 2010 18:25:00 +0530 > Harsh Prateek Bora<harsh@linux.vnet.ibm.com> wrote: > >> The existing code causes the if condition to pass when it should fail >> on a *64-bit kernel* because of implicit data type conversions. It can >> be observed by passing pos = -1 and count = some positive number. >> This results in function returning EOVERFLOW instead of EINVAL. >> >> With this patch, the function returns EINVAL when pos is -1 and count >> is a positive number. This can be tested by calling sendfile with >> offset = -1 and count = some positive number on a 64-bit kernel. >> > > Hmm, is this clearer ? > > == > > commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for > negative (unsigned) page offset for very large files as /proc/<pid>/mem > and /dev/mem. > > In that patch, overlap check routine is added but it was wrong. > > Considering 'pos' is loff_t, a signed value, > > In usual case, at comparing 'pos' and 'pos+count' > > (positive) / (positive) OK > (positive) / (nevative) EOVERFLOW > (negative) / (positive) EINVAL > (negative) / (negative) EINVAL > > In FMODE_UNSIGNED_OFFSET case, > > (positive) / (positive) OK > (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) > (nevative) / (negative) OK > (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) > > Signed-off-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > > --- > fs/read_write.c | 21 +++++++++++++++++---- > 1 file changed, 17 insertions(+), 4 deletions(-) > > Index: linux-2.6.37-rc5/fs/read_write.c > =================================================================== > --- linux-2.6.37-rc5.orig/fs/read_write.c > +++ linux-2.6.37-rc5/fs/read_write.c > @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, > * pos or pos+count is negative here, check overflow. > * too big "count" will be caught in rw_verify_area(). > */ > - if ((pos< 0)&& (pos + count< pos)) > + /* negative pos is allowed only when the flag is set */ > + if (!(file->f_mode& FMODE_UNSIGNED_OFFSET)) { > + if ((pos> 0)&& (pos + count> 0)) > + return 0; > + if ((pos> 0)&& (pos + count< 0)) > + return -EOVERFLOW; > + return -EINVAL; > + } > + /* > + * The file supports 'unsigned long' offset. (but loff_t is signed) > + * When pos is negative, -1 is the biggest number. So if pos + count > + * is larger than pos, it's overflow. > + * (ex) -1 + 10 = 9 ...means > + * 0xffff + 0xa = 0x9 => overflow. > + */ > + if ((pos< 0)&& (pos + count> 0)) Well, that works fine for what I am concerned but I think there is a mismatch in the code and the comment above. As per the comments above, it should be like: if ((pos < 0) && (pos + count > pos)) Regards, Harsh. > return -EOVERFLOW; > - if (file->f_mode& FMODE_UNSIGNED_OFFSET) > - return 0; > - return -EINVAL; > + return 0; > } > > /** > > -- > To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 6:39 ` Harsh Bora @ 2010-12-10 7:01 ` KAMEZAWA Hiroyuki 2010-12-10 7:18 ` Harsh Bora 0 siblings, 1 reply; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-10 7:01 UTC (permalink / raw) To: Harsh Bora; +Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Fri, 10 Dec 2010 12:09:42 +0530 Harsh Bora <harsh@linux.vnet.ibm.com> wrote: return -EINVAL; > > + } > > + /* > > + * The file supports 'unsigned long' offset. (but loff_t is signed) > > + * When pos is negative, -1 is the biggest number. So if pos + count > > + * is larger than pos, it's overflow. > > + * (ex) -1 + 10 = 9 ...means > > + * 0xffff + 0xa = 0x9 => overflow. > > + */ > > + if ((pos< 0)&& (pos + count> 0)) > > Well, that works fine for what I am concerned but I think there is a > mismatch in the code and the comment above. As per the comments above, > it should be like: > if ((pos < 0) && (pos + count > pos)) > Ah, yes. updated. Thank you for review and test. -Kame == commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for negative (unsigned) page offset for very large files as /proc/<pid>/mem and /dev/mem. In that patch, overlap check routine is added but it was wrong. Considering 'pos' is loff_t, a signed value, In usual case, at comparing 'pos' and 'pos+count' (positive) / (positive) OK (positive) / (nevative) EOVERFLOW (negative) / (positive) EINVAL (negative) / (negative) EINVAL In FMODE_UNSIGNED_OFFSET case, (positive) / (positive) OK (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) (nevative) / (negative) OK (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) Changelog: - fixed a comment. Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> --- fs/read_write.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) Index: linux-2.6.37-rc5/fs/read_write.c =================================================================== --- linux-2.6.37-rc5.orig/fs/read_write.c +++ linux-2.6.37-rc5/fs/read_write.c @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, * pos or pos+count is negative here, check overflow. * too big "count" will be caught in rw_verify_area(). */ - if ((pos < 0) && (pos + count < pos)) + /* negative pos is allowed only when the flag is set */ + if (!(file->f_mode & FMODE_UNSIGNED_OFFSET)) { + if ((pos > 0) && (pos + count > 0)) + return 0; + if ((pos > 0) && (pos + count < 0)) + return -EOVERFLOW; + return -EINVAL; + } + /* + * The file supports 'unsigned long' offset. (but loff_t is signed) + * When pos is negative, -1 is the biggest number. So if pos + count + * is larger than 0, it's overflow. + * (ex) -1 + 10 = 9 ...means + * 0xffff + 0xa = 0x9 => overflow. + */ + if ((pos < 0) && (pos + count > 0)) return -EOVERFLOW; - if (file->f_mode & FMODE_UNSIGNED_OFFSET) - return 0; - return -EINVAL; + return 0; } /** ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 7:01 ` KAMEZAWA Hiroyuki @ 2010-12-10 7:18 ` Harsh Bora 2010-12-10 7:59 ` KAMEZAWA Hiroyuki 0 siblings, 1 reply; 15+ messages in thread From: Harsh Bora @ 2010-12-10 7:18 UTC (permalink / raw) To: KAMEZAWA Hiroyuki; +Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On 12/10/2010 12:31 PM, KAMEZAWA Hiroyuki wrote: > On Fri, 10 Dec 2010 12:09:42 +0530 > Harsh Bora<harsh@linux.vnet.ibm.com> wrote: > return -EINVAL; >>> + } >>> + /* >>> + * The file supports 'unsigned long' offset. (but loff_t is signed) >>> + * When pos is negative, -1 is the biggest number. So if pos + count >>> + * is larger than pos, it's overflow. >>> + * (ex) -1 + 10 = 9 ...means >>> + * 0xffff + 0xa = 0x9 => overflow. >>> + */ >>> + if ((pos< 0)&& (pos + count> 0)) >> >> Well, that works fine for what I am concerned but I think there is a >> mismatch in the code and the comment above. As per the comments above, >> it should be like: >> if ((pos< 0)&& (pos + count> pos)) >> > > Ah, yes. updated. Thank you for review and test. > -Kame > == > commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for > negative (unsigned) page offset for very large files as /proc/<pid>/mem > and /dev/mem. > > In that patch, overlap check routine is added but it was wrong. > > Considering 'pos' is loff_t, a signed value, > > In usual case, at comparing 'pos' and 'pos+count' > > (positive) / (positive) OK > (positive) / (nevative) EOVERFLOW > (negative) / (positive) EINVAL > (negative) / (negative) EINVAL > > In FMODE_UNSIGNED_OFFSET case, > > (positive) / (positive) OK > (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) > (nevative) / (negative) OK > (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) > > Changelog: > - fixed a comment. > > Signed-off-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > > --- > fs/read_write.c | 21 +++++++++++++++++---- > 1 file changed, 17 insertions(+), 4 deletions(-) > > Index: linux-2.6.37-rc5/fs/read_write.c > =================================================================== > --- linux-2.6.37-rc5.orig/fs/read_write.c > +++ linux-2.6.37-rc5/fs/read_write.c > @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, > * pos or pos+count is negative here, check overflow. > * too big "count" will be caught in rw_verify_area(). > */ > - if ((pos< 0)&& (pos + count< pos)) > + /* negative pos is allowed only when the flag is set */ > + if (!(file->f_mode& FMODE_UNSIGNED_OFFSET)) { > + if ((pos> 0)&& (pos + count> 0)) Do we really need 2 checks? If first one is true, second one has to be true for count being unsigned? > + return 0; > + if ((pos> 0)&& (pos + count< 0)) BTW, when will the above condition be true ? As if first condition is true, the second cant be true, as the count is unsigned. Sorry for the lazy review .. Regards, Harsh > + return -EOVERFLOW; > + return -EINVAL; > + } > + /* > + * The file supports 'unsigned long' offset. (but loff_t is signed) > + * When pos is negative, -1 is the biggest number. So if pos + count > + * is larger than 0, it's overflow. > + * (ex) -1 + 10 = 9 ...means > + * 0xffff + 0xa = 0x9 => overflow. > + */ > + if ((pos< 0)&& (pos + count> 0)) > return -EOVERFLOW; > - if (file->f_mode& FMODE_UNSIGNED_OFFSET) > - return 0; > - return -EINVAL; > + return 0; > } > > /** > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 7:18 ` Harsh Bora @ 2010-12-10 7:59 ` KAMEZAWA Hiroyuki 2010-12-10 8:13 ` Harsh Bora 0 siblings, 1 reply; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-10 7:59 UTC (permalink / raw) To: Harsh Bora; +Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Fri, 10 Dec 2010 12:48:05 +0530 Harsh Bora <harsh@linux.vnet.ibm.com> wrote: > On 12/10/2010 12:31 PM, KAMEZAWA Hiroyuki wrote: > > On Fri, 10 Dec 2010 12:09:42 +0530 > > Harsh Bora<harsh@linux.vnet.ibm.com> wrote: > > return -EINVAL; > >>> + } > >>> + /* > >>> + * The file supports 'unsigned long' offset. (but loff_t is signed) > >>> + * When pos is negative, -1 is the biggest number. So if pos + count > >>> + * is larger than pos, it's overflow. > >>> + * (ex) -1 + 10 = 9 ...means > >>> + * 0xffff + 0xa = 0x9 => overflow. > >>> + */ > >>> + if ((pos< 0)&& (pos + count> 0)) > >> > >> Well, that works fine for what I am concerned but I think there is a > >> mismatch in the code and the comment above. As per the comments above, > >> it should be like: > >> if ((pos< 0)&& (pos + count> pos)) > >> > > > > Ah, yes. updated. Thank you for review and test. > > -Kame > > == > > commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for > > negative (unsigned) page offset for very large files as /proc/<pid>/mem > > and /dev/mem. > > > > In that patch, overlap check routine is added but it was wrong. > > > > Considering 'pos' is loff_t, a signed value, > > > > In usual case, at comparing 'pos' and 'pos+count' > > > > (positive) / (positive) OK > > (positive) / (nevative) EOVERFLOW > > (negative) / (positive) EINVAL > > (negative) / (negative) EINVAL > > > > In FMODE_UNSIGNED_OFFSET case, > > > > (positive) / (positive) OK > > (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) > > (nevative) / (negative) OK > > (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) > > > > Changelog: > > - fixed a comment. > > > > Signed-off-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > > > > --- > > fs/read_write.c | 21 +++++++++++++++++---- > > 1 file changed, 17 insertions(+), 4 deletions(-) > > > > Index: linux-2.6.37-rc5/fs/read_write.c > > =================================================================== > > --- linux-2.6.37-rc5.orig/fs/read_write.c > > +++ linux-2.6.37-rc5/fs/read_write.c > > @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, > > * pos or pos+count is negative here, check overflow. > > * too big "count" will be caught in rw_verify_area(). > > */ > > - if ((pos< 0)&& (pos + count< pos)) > > + /* negative pos is allowed only when the flag is set */ > > + if (!(file->f_mode& FMODE_UNSIGNED_OFFSET)) { > > + if ((pos> 0)&& (pos + count> 0)) Hmm. > Do we really need 2 checks? If first one is true, second one has to be > true for count being unsigned? pos is signed value. Then, if pos is near to LOGN_MAX, pos+count can be < 0. > > + return 0; > > + if ((pos> 0)&& (pos + count< 0)) > BTW, when will the above condition be true ? As if first condition is > true, the second cant be true, as the count is unsigned. > Ah, hmm, type casting problem ? (signed) + (unsigned) => (unsigned) ah, ok. count should be signed... Is this messy ? == commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for negative (unsigned) page offset for very large files as /proc/<pid>/mem and /dev/mem. In that patch, overlap check routine is added but it was wrong. Considering 'pos' is loff_t, a signed value, In usual case, at comparing 'pos' and 'pos+count' (positive) / (positive) OK (positive) / (nevative) EOVERFLOW (negative) / (positive) EINVAL (negative) / (negative) EINVAL In FMODE_UNSIGNED_OFFSET case, (positive) / (positive) OK (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) (nevative) / (negative) OK (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) Changelog v1->v2: - fixed signed+unsigned=unsigned problem. Changelog v0->v1: - fixed a comment. Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> --- fs/read_write.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) Index: linux-2.6.37-rc5/fs/read_write.c =================================================================== --- linux-2.6.37-rc5.orig/fs/read_write.c +++ linux-2.6.37-rc5/fs/read_write.c @@ -33,15 +33,31 @@ EXPORT_SYMBOL(generic_ro_fops); static int __negative_fpos_check(struct file *file, loff_t pos, size_t count) { + ssize_t len = (ssize_t)count; + /* len > 0 is checked before this call. */ + BUG_ON(len < 0); /* * pos or pos+count is negative here, check overflow. * too big "count" will be caught in rw_verify_area(). */ - if ((pos < 0) && (pos + count < pos)) + /* negative pos is allowed only when the flag is set */ + if (!(file->f_mode & FMODE_UNSIGNED_OFFSET)) { + if ((pos > 0) && (pos + len > 0)) + return 0; + if ((pos > 0) && (pos + len < 0)) + return -EOVERFLOW; + return -EINVAL; + } + /* + * The file supports 'unsigned long' offset. (but loff_t is signed) + * When pos is negative, -1 is the biggest number. So if pos + count + * is larger than 0, it's overflow. + * (ex) -1 + 10 = 9 ...means + * 0xffff + 0xa = 0x9 => overflow. + */ + if ((pos < 0) && (pos + len > 0)) return -EOVERFLOW; - if (file->f_mode & FMODE_UNSIGNED_OFFSET) - return 0; - return -EINVAL; + return 0; } /** ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 7:59 ` KAMEZAWA Hiroyuki @ 2010-12-10 8:13 ` Harsh Bora 2010-12-10 8:20 ` KAMEZAWA Hiroyuki 0 siblings, 1 reply; 15+ messages in thread From: Harsh Bora @ 2010-12-10 8:13 UTC (permalink / raw) To: KAMEZAWA Hiroyuki Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao, M. Mohan Kumar On 12/10/2010 01:29 PM, KAMEZAWA Hiroyuki wrote: > On Fri, 10 Dec 2010 12:48:05 +0530 > Harsh Bora<harsh@linux.vnet.ibm.com> wrote: > >> On 12/10/2010 12:31 PM, KAMEZAWA Hiroyuki wrote: >>> On Fri, 10 Dec 2010 12:09:42 +0530 >>> Harsh Bora<harsh@linux.vnet.ibm.com> wrote: >>> return -EINVAL; >>>>> + } >>>>> + /* >>>>> + * The file supports 'unsigned long' offset. (but loff_t is signed) >>>>> + * When pos is negative, -1 is the biggest number. So if pos + count >>>>> + * is larger than pos, it's overflow. >>>>> + * (ex) -1 + 10 = 9 ...means >>>>> + * 0xffff + 0xa = 0x9 => overflow. >>>>> + */ >>>>> + if ((pos< 0)&& (pos + count> 0)) >>>> >>>> Well, that works fine for what I am concerned but I think there is a >>>> mismatch in the code and the comment above. As per the comments above, >>>> it should be like: >>>> if ((pos< 0)&& (pos + count> pos)) >>>> >>> >>> Ah, yes. updated. Thank you for review and test. >>> -Kame >>> == >>> commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for >>> negative (unsigned) page offset for very large files as /proc/<pid>/mem >>> and /dev/mem. >>> >>> In that patch, overlap check routine is added but it was wrong. >>> >>> Considering 'pos' is loff_t, a signed value, >>> >>> In usual case, at comparing 'pos' and 'pos+count' >>> >>> (positive) / (positive) OK >>> (positive) / (nevative) EOVERFLOW >>> (negative) / (positive) EINVAL >>> (negative) / (negative) EINVAL >>> >>> In FMODE_UNSIGNED_OFFSET case, >>> >>> (positive) / (positive) OK >>> (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) >>> (nevative) / (negative) OK >>> (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) >>> >>> Changelog: >>> - fixed a comment. >>> >>> Signed-off-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> >>> >>> --- >>> fs/read_write.c | 21 +++++++++++++++++---- >>> 1 file changed, 17 insertions(+), 4 deletions(-) >>> >>> Index: linux-2.6.37-rc5/fs/read_write.c >>> =================================================================== >>> --- linux-2.6.37-rc5.orig/fs/read_write.c >>> +++ linux-2.6.37-rc5/fs/read_write.c >>> @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, >>> * pos or pos+count is negative here, check overflow. >>> * too big "count" will be caught in rw_verify_area(). >>> */ >>> - if ((pos< 0)&& (pos + count< pos)) >>> + /* negative pos is allowed only when the flag is set */ >>> + if (!(file->f_mode& FMODE_UNSIGNED_OFFSET)) { >>> + if ((pos> 0)&& (pos + count> 0)) > Hmm. > >> Do we really need 2 checks? If first one is true, second one has to be >> true for count being unsigned? > > pos is signed value. Then, if pos is near to LOGN_MAX, pos+count can be< 0. Well, if you mean that, you need to typecast. Going back to what I proposed, you need to put it like that: if ((pos> 0)&& ( (loff_t) (pos + count) > 0)) otherwise, the result of pos + count becomes an unsigned value on a 64 bit system .. > > >>> + return 0; >>> + if ((pos> 0)&& (pos + count< 0)) >> BTW, when will the above condition be true ? As if first condition is >> true, the second cant be true, as the count is unsigned. >> > Ah, hmm, type casting problem ? > > (signed) + (unsigned) => (unsigned) > > ah, ok. count should be signed... No, count shouldnt be signed, you may guess why. typecating the sum to loff_t is the solution. Regards, Harsh > Is this messy ? > == > commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for > negative (unsigned) page offset for very large files as /proc/<pid>/mem > and /dev/mem. > > In that patch, overlap check routine is added but it was wrong. > > Considering 'pos' is loff_t, a signed value, > > In usual case, at comparing 'pos' and 'pos+count' > > (positive) / (positive) OK > (positive) / (nevative) EOVERFLOW > (negative) / (positive) EINVAL > (negative) / (negative) EINVAL > > In FMODE_UNSIGNED_OFFSET case, > > (positive) / (positive) OK > (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) > (nevative) / (negative) OK > (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) > > Changelog v1->v2: > - fixed signed+unsigned=unsigned problem. > Changelog v0->v1: > - fixed a comment. > > Signed-off-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > > --- > fs/read_write.c | 24 ++++++++++++++++++++---- > 1 file changed, 20 insertions(+), 4 deletions(-) > > Index: linux-2.6.37-rc5/fs/read_write.c > =================================================================== > --- linux-2.6.37-rc5.orig/fs/read_write.c > +++ linux-2.6.37-rc5/fs/read_write.c > @@ -33,15 +33,31 @@ EXPORT_SYMBOL(generic_ro_fops); > static int > __negative_fpos_check(struct file *file, loff_t pos, size_t count) > { > + ssize_t len = (ssize_t)count; > + /* len> 0 is checked before this call. */ > + BUG_ON(len< 0); > /* > * pos or pos+count is negative here, check overflow. > * too big "count" will be caught in rw_verify_area(). > */ > - if ((pos< 0)&& (pos + count< pos)) > + /* negative pos is allowed only when the flag is set */ > + if (!(file->f_mode& FMODE_UNSIGNED_OFFSET)) { > + if ((pos> 0)&& (pos + len> 0)) > + return 0; > + if ((pos> 0)&& (pos + len< 0)) > + return -EOVERFLOW; > + return -EINVAL; > + } > + /* > + * The file supports 'unsigned long' offset. (but loff_t is signed) > + * When pos is negative, -1 is the biggest number. So if pos + count > + * is larger than 0, it's overflow. > + * (ex) -1 + 10 = 9 ...means > + * 0xffff + 0xa = 0x9 => overflow. > + */ > + if ((pos< 0)&& (pos + len> 0)) > return -EOVERFLOW; > - if (file->f_mode& FMODE_UNSIGNED_OFFSET) > - return 0; > - return -EINVAL; > + return 0; > } > > /** > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 8:13 ` Harsh Bora @ 2010-12-10 8:20 ` KAMEZAWA Hiroyuki 0 siblings, 0 replies; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-10 8:20 UTC (permalink / raw) To: Harsh Bora Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao, M. Mohan Kumar On Fri, 10 Dec 2010 13:43:26 +0530 Harsh Bora <harsh@linux.vnet.ibm.com> wrote: > On 12/10/2010 01:29 PM, KAMEZAWA Hiroyuki wrote: > > On Fri, 10 Dec 2010 12:48:05 +0530 > > Harsh Bora<harsh@linux.vnet.ibm.com> wrote: > > > >> On 12/10/2010 12:31 PM, KAMEZAWA Hiroyuki wrote: > >>> On Fri, 10 Dec 2010 12:09:42 +0530 > >>> Harsh Bora<harsh@linux.vnet.ibm.com> wrote: > >>> return -EINVAL; > >>>>> + } > >>>>> + /* > >>>>> + * The file supports 'unsigned long' offset. (but loff_t is signed) > >>>>> + * When pos is negative, -1 is the biggest number. So if pos + count > >>>>> + * is larger than pos, it's overflow. > >>>>> + * (ex) -1 + 10 = 9 ...means > >>>>> + * 0xffff + 0xa = 0x9 => overflow. > >>>>> + */ > >>>>> + if ((pos< 0)&& (pos + count> 0)) > >>>> > >>>> Well, that works fine for what I am concerned but I think there is a > >>>> mismatch in the code and the comment above. As per the comments above, > >>>> it should be like: > >>>> if ((pos< 0)&& (pos + count> pos)) > >>>> > >>> > >>> Ah, yes. updated. Thank you for review and test. > >>> -Kame > >>> == > >>> commit 4a3956c790290efeb647bbb0c3a90476bb57800e adds support for > >>> negative (unsigned) page offset for very large files as /proc/<pid>/mem > >>> and /dev/mem. > >>> > >>> In that patch, overlap check routine is added but it was wrong. > >>> > >>> Considering 'pos' is loff_t, a signed value, > >>> > >>> In usual case, at comparing 'pos' and 'pos+count' > >>> > >>> (positive) / (positive) OK > >>> (positive) / (nevative) EOVERFLOW > >>> (negative) / (positive) EINVAL > >>> (negative) / (negative) EINVAL > >>> > >>> In FMODE_UNSIGNED_OFFSET case, > >>> > >>> (positive) / (positive) OK > >>> (positive) / (nevative) OK (ex. 0x7fff -> 0x8000) > >>> (nevative) / (negative) OK > >>> (negative) / (positive) EOVERFLOW (ex. 0xffff -> 0x1) > >>> > >>> Changelog: > >>> - fixed a comment. > >>> > >>> Signed-off-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > >>> > >>> --- > >>> fs/read_write.c | 21 +++++++++++++++++---- > >>> 1 file changed, 17 insertions(+), 4 deletions(-) > >>> > >>> Index: linux-2.6.37-rc5/fs/read_write.c > >>> =================================================================== > >>> --- linux-2.6.37-rc5.orig/fs/read_write.c > >>> +++ linux-2.6.37-rc5/fs/read_write.c > >>> @@ -37,11 +37,24 @@ __negative_fpos_check(struct file *file, > >>> * pos or pos+count is negative here, check overflow. > >>> * too big "count" will be caught in rw_verify_area(). > >>> */ > >>> - if ((pos< 0)&& (pos + count< pos)) > >>> + /* negative pos is allowed only when the flag is set */ > >>> + if (!(file->f_mode& FMODE_UNSIGNED_OFFSET)) { > >>> + if ((pos> 0)&& (pos + count> 0)) > > Hmm. > > > >> Do we really need 2 checks? If first one is true, second one has to be > >> true for count being unsigned? > > > > pos is signed value. Then, if pos is near to LOGN_MAX, pos+count can be< 0. > > Well, if you mean that, you need to typecast. Going back to what I > proposed, you need to put it like that: > if ((pos> 0)&& ( (loff_t) (pos + count) > 0)) > otherwise, the result of pos + count becomes an unsigned value on a 64 > bit system .. > > > > > >>> + return 0; > >>> + if ((pos> 0)&& (pos + count< 0)) > >> BTW, when will the above condition be true ? As if first condition is > >> true, the second cant be true, as the count is unsigned. > >> > > Ah, hmm, type casting problem ? > > > > (signed) + (unsigned) => (unsigned) > > > > ah, ok. count should be signed... > No, count shouldnt be signed, you may guess why. typecating the sum to > loff_t is the solution. > Ok, I acked to your patch. Thank you for explaining. Thanks, -Kame ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-08 12:55 [PATCH] Typecasting required for comparing unlike datatypes Harsh Prateek Bora 2010-12-09 18:32 ` Harsh Bora 2010-12-10 0:53 ` KAMEZAWA Hiroyuki @ 2010-12-10 8:18 ` KAMEZAWA Hiroyuki 2010-12-10 8:31 ` Harsh Bora 2010-12-15 9:50 ` Al Viro 2 siblings, 2 replies; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-10 8:18 UTC (permalink / raw) To: Harsh Prateek Bora; +Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Wed, 8 Dec 2010 18:25:00 +0530 Harsh Prateek Bora <harsh@linux.vnet.ibm.com> wrote: > The existing code causes the if condition to pass when it should fail > on a *64-bit kernel* because of implicit data type conversions. It can > be observed by passing pos = -1 and count = some positive number. > This results in function returning EOVERFLOW instead of EINVAL. > > With this patch, the function returns EINVAL when pos is -1 and count > is a positive number. This can be tested by calling sendfile with > offset = -1 and count = some positive number on a 64-bit kernel. > > Signed-off-by: Harsh Prateek Bora <harsh@linux.vnet.ibm.com> Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> I'm sorry for annoying you. > --- > fs/read_write.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/fs/read_write.c b/fs/read_write.c > index 431a0ed..a8eabd4 100644 > --- a/fs/read_write.c > +++ b/fs/read_write.c > @@ -38,7 +38,7 @@ __negative_fpos_check(struct file *file, loff_t pos, size_t count) > * pos or pos+count is negative here, check overflow. > * too big "count" will be caught in rw_verify_area(). > */ > - if ((pos < 0) && (pos + count < pos)) > + if ((pos < 0) && ( (loff_t) (pos + count) < pos)) > return -EOVERFLOW; > if (file->f_mode & FMODE_UNSIGNED_OFFSET) > return 0; > -- > 1.7.1.1 > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 8:18 ` KAMEZAWA Hiroyuki @ 2010-12-10 8:31 ` Harsh Bora 2010-12-15 9:50 ` Al Viro 1 sibling, 0 replies; 15+ messages in thread From: Harsh Bora @ 2010-12-10 8:31 UTC (permalink / raw) To: KAMEZAWA Hiroyuki Cc: linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao, M. Mohan Kumar On 12/10/2010 01:48 PM, KAMEZAWA Hiroyuki wrote: > On Wed, 8 Dec 2010 18:25:00 +0530 > Harsh Prateek Bora<harsh@linux.vnet.ibm.com> wrote: > >> The existing code causes the if condition to pass when it should fail >> on a *64-bit kernel* because of implicit data type conversions. It can >> be observed by passing pos = -1 and count = some positive number. >> This results in function returning EOVERFLOW instead of EINVAL. >> >> With this patch, the function returns EINVAL when pos is -1 and count >> is a positive number. This can be tested by calling sendfile with >> offset = -1 and count = some positive number on a 64-bit kernel. >> >> Signed-off-by: Harsh Prateek Bora<harsh@linux.vnet.ibm.com> > > Acked-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > > I'm sorry for annoying you. > Oh, but I am not annoyed, .. Thanks for the ack though ! :) >> --- >> fs/read_write.c | 2 +- >> 1 files changed, 1 insertions(+), 1 deletions(-) >> >> diff --git a/fs/read_write.c b/fs/read_write.c >> index 431a0ed..a8eabd4 100644 >> --- a/fs/read_write.c >> +++ b/fs/read_write.c >> @@ -38,7 +38,7 @@ __negative_fpos_check(struct file *file, loff_t pos, size_t count) >> * pos or pos+count is negative here, check overflow. >> * too big "count" will be caught in rw_verify_area(). >> */ >> - if ((pos< 0)&& (pos + count< pos)) >> + if ((pos< 0)&& ( (loff_t) (pos + count)< pos)) >> return -EOVERFLOW; >> if (file->f_mode& FMODE_UNSIGNED_OFFSET) >> return 0; >> -- >> 1.7.1.1 >> >> > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-10 8:18 ` KAMEZAWA Hiroyuki 2010-12-10 8:31 ` Harsh Bora @ 2010-12-15 9:50 ` Al Viro 2010-12-16 0:24 ` KAMEZAWA Hiroyuki 1 sibling, 1 reply; 15+ messages in thread From: Al Viro @ 2010-12-15 9:50 UTC (permalink / raw) To: KAMEZAWA Hiroyuki Cc: Harsh Prateek Bora, linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Fri, Dec 10, 2010 at 05:18:51PM +0900, KAMEZAWA Hiroyuki wrote: > On Wed, 8 Dec 2010 18:25:00 +0530 > Harsh Prateek Bora <harsh@linux.vnet.ibm.com> wrote: > > > The existing code causes the if condition to pass when it should fail > > on a *64-bit kernel* because of implicit data type conversions. It can > > be observed by passing pos = -1 and count = some positive number. > > This results in function returning EOVERFLOW instead of EINVAL. > > > > With this patch, the function returns EINVAL when pos is -1 and count > > is a positive number. This can be tested by calling sendfile with > > offset = -1 and count = some positive number on a 64-bit kernel. > > > > Signed-off-by: Harsh Prateek Bora <harsh@linux.vnet.ibm.com> > > Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> > > I'm sorry for annoying you. NAK. Look at what's getting done there; this check is the _only_ place in function that uses count, it is constantly false if count is zero _and_ we have only one caller that passes non-zero. Moreover, in case of count being zero we ignore offset as well. Obvious things to do: a) take the check in question into the only caller that would pass non-zero count (i.e. rw_verify_area()) b) lose the second and the third arguments of __negative_fpos_check() c) sort out what the hell should be done in that place. Note that current logics is very convoluted. First of all, we rely on loff_t being long long in the kernel and size_t being not bigger than unsigned long long. See ((loff_t)(pos + count) < 0) in there - if count gets wider than pos, we suddenly get all kinds of odd crap. That's OK; if we run into ports with size_t wider than 64 bits, we'll have more trouble anyway. So what we have for signed offset case is pos >= 0, count <= max loff_t - pos. Fair enough. For unsigned offset case it's more interesting - we want to allow pos < 0, and we just want to prohibit wraparounds from negative to positive. IOW, it's pos >= 0 || count < -pos; note that we already have count <= max ssize_t, which we assume to be no more than max loff_t. So what we need is this: if (unlikely(pos < 0)) { if it's signed -EINVAL if (count >= -pos) /* both values are in 0..LLONG_MAX */ -EOVERFLOW } else if (unlikely((loff_t)(pos + count) < 0)) { if it's signed -EINVAL } and we are done with that. IOW, how about the patch below? Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> --- diff --git a/fs/read_write.c b/fs/read_write.c index 5d431ba..5520f8a 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -30,18 +30,9 @@ const struct file_operations generic_ro_fops = { EXPORT_SYMBOL(generic_ro_fops); -static int -__negative_fpos_check(struct file *file, loff_t pos, size_t count) +static inline int unsigned_offsets(struct file *file) { - /* - * pos or pos+count is negative here, check overflow. - * too big "count" will be caught in rw_verify_area(). - */ - if ((pos < 0) && (pos + count < pos)) - return -EOVERFLOW; - if (file->f_mode & FMODE_UNSIGNED_OFFSET) - return 0; - return -EINVAL; + return file->f_mode & FMODE_UNSIGNED_OFFSET; } /** @@ -75,7 +66,7 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) break; } - if (offset < 0 && __negative_fpos_check(file, offset, 0)) + if (offset < 0 && !unsigned_offsets(file)) return -EINVAL; if (offset > inode->i_sb->s_maxbytes) return -EINVAL; @@ -152,7 +143,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) offset += file->f_pos; } retval = -EINVAL; - if (offset >= 0 || !__negative_fpos_check(file, offset, 0)) { + if (offset >= 0 || unsigned_offsets(file)) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; @@ -252,9 +243,13 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count if (unlikely((ssize_t) count < 0)) return retval; pos = *ppos; - if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) { - retval = __negative_fpos_check(file, pos, count); - if (retval) + if (unlikely(pos < 0)) { + if (!unsigned_offsets(file)) + return retval; + if (count >= -pos) /* both values are in 0..LLONG_MAX */ + return -EOVERFLOW; + } else if (unlikely((loff_t) (pos + count) < 0)) { + if (!unsigned_offsets(file)) return retval; } ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-15 9:50 ` Al Viro @ 2010-12-16 0:24 ` KAMEZAWA Hiroyuki 2010-12-19 7:02 ` Harsh Bora 0 siblings, 1 reply; 15+ messages in thread From: KAMEZAWA Hiroyuki @ 2010-12-16 0:24 UTC (permalink / raw) To: Al Viro Cc: Harsh Prateek Bora, linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On Wed, 15 Dec 2010 09:50:24 +0000 Al Viro <viro@ZenIV.linux.org.uk> wrote: > On Fri, Dec 10, 2010 at 05:18:51PM +0900, KAMEZAWA Hiroyuki wrote: > > On Wed, 8 Dec 2010 18:25:00 +0530 > > Harsh Prateek Bora <harsh@linux.vnet.ibm.com> wrote: > > > > > The existing code causes the if condition to pass when it should fail > > > on a *64-bit kernel* because of implicit data type conversions. It can > > > be observed by passing pos = -1 and count = some positive number. > > > This results in function returning EOVERFLOW instead of EINVAL. > > > > > > With this patch, the function returns EINVAL when pos is -1 and count > > > is a positive number. This can be tested by calling sendfile with > > > offset = -1 and count = some positive number on a 64-bit kernel. > > > > > > Signed-off-by: Harsh Prateek Bora <harsh@linux.vnet.ibm.com> > > > > Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> > > > > I'm sorry for annoying you. > > NAK. Look at what's getting done there; this check is the _only_ place > in function that uses count, it is constantly false if count is zero > _and_ we have only one caller that passes non-zero. Moreover, in case of > count being zero we ignore offset as well. > > Obvious things to do: > a) take the check in question into the only caller that would > pass non-zero count (i.e. rw_verify_area()) > b) lose the second and the third arguments of __negative_fpos_check() > c) sort out what the hell should be done in that place. > > Note that current logics is very convoluted. First of all, we rely on > loff_t being long long in the kernel and size_t being not bigger than > unsigned long long. See ((loff_t)(pos + count) < 0) in there - if count > gets wider than pos, we suddenly get all kinds of odd crap. > > That's OK; if we run into ports with size_t wider than 64 bits, we'll have > more trouble anyway. > > So what we have for signed offset case is pos >= 0, count <= max loff_t - pos. > Fair enough. For unsigned offset case it's more interesting - we want to > allow pos < 0, and we just want to prohibit wraparounds from negative to > positive. IOW, it's pos >= 0 || count < -pos; note that we already have > count <= max ssize_t, which we assume to be no more than max loff_t. > > So what we need is this: > if (unlikely(pos < 0)) { > if it's signed > -EINVAL > if (count >= -pos) /* both values are in 0..LLONG_MAX */ > -EOVERFLOW > } else if (unlikely((loff_t)(pos + count) < 0)) { > if it's signed > -EINVAL > } > and we are done with that. IOW, how about the patch below? > > > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> This seems much easier to read. thank you. Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> and sorry for my 1st implemenation. > --- > diff --git a/fs/read_write.c b/fs/read_write.c > index 5d431ba..5520f8a 100644 > --- a/fs/read_write.c > +++ b/fs/read_write.c > @@ -30,18 +30,9 @@ const struct file_operations generic_ro_fops = { > > EXPORT_SYMBOL(generic_ro_fops); > > -static int > -__negative_fpos_check(struct file *file, loff_t pos, size_t count) > +static inline int unsigned_offsets(struct file *file) > { > - /* > - * pos or pos+count is negative here, check overflow. > - * too big "count" will be caught in rw_verify_area(). > - */ > - if ((pos < 0) && (pos + count < pos)) > - return -EOVERFLOW; > - if (file->f_mode & FMODE_UNSIGNED_OFFSET) > - return 0; > - return -EINVAL; > + return file->f_mode & FMODE_UNSIGNED_OFFSET; > } > > /** > @@ -75,7 +66,7 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) > break; > } > > - if (offset < 0 && __negative_fpos_check(file, offset, 0)) > + if (offset < 0 && !unsigned_offsets(file)) > return -EINVAL; > if (offset > inode->i_sb->s_maxbytes) > return -EINVAL; > @@ -152,7 +143,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) > offset += file->f_pos; > } > retval = -EINVAL; > - if (offset >= 0 || !__negative_fpos_check(file, offset, 0)) { > + if (offset >= 0 || unsigned_offsets(file)) { > if (offset != file->f_pos) { > file->f_pos = offset; > file->f_version = 0; > @@ -252,9 +243,13 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count > if (unlikely((ssize_t) count < 0)) > return retval; > pos = *ppos; > - if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) { > - retval = __negative_fpos_check(file, pos, count); > - if (retval) > + if (unlikely(pos < 0)) { > + if (!unsigned_offsets(file)) > + return retval; > + if (count >= -pos) /* both values are in 0..LLONG_MAX */ > + return -EOVERFLOW; > + } else if (unlikely((loff_t) (pos + count) < 0)) { > + if (!unsigned_offsets(file)) > return retval; > } > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] Typecasting required for comparing unlike datatypes 2010-12-16 0:24 ` KAMEZAWA Hiroyuki @ 2010-12-19 7:02 ` Harsh Bora 0 siblings, 0 replies; 15+ messages in thread From: Harsh Bora @ 2010-12-19 7:02 UTC (permalink / raw) To: Al Viro; +Cc: KAMEZAWA Hiroyuki, linux-fsdevel, fengguang.wu, aneesh.kumar, jvrao On 12/16/2010 05:54 AM, KAMEZAWA Hiroyuki wrote: > On Wed, 15 Dec 2010 09:50:24 +0000 > Al Viro<viro@ZenIV.linux.org.uk> wrote: > >> On Fri, Dec 10, 2010 at 05:18:51PM +0900, KAMEZAWA Hiroyuki wrote: >>> On Wed, 8 Dec 2010 18:25:00 +0530 >>> Harsh Prateek Bora<harsh@linux.vnet.ibm.com> wrote: >>> >>>> The existing code causes the if condition to pass when it should fail >>>> on a *64-bit kernel* because of implicit data type conversions. It can >>>> be observed by passing pos = -1 and count = some positive number. >>>> This results in function returning EOVERFLOW instead of EINVAL. >>>> >>>> With this patch, the function returns EINVAL when pos is -1 and count >>>> is a positive number. This can be tested by calling sendfile with >>>> offset = -1 and count = some positive number on a 64-bit kernel. >>>> >>>> Signed-off-by: Harsh Prateek Bora<harsh@linux.vnet.ibm.com> >>> >>> Acked-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> >>> >>> I'm sorry for annoying you. >> >> NAK. Look at what's getting done there; this check is the _only_ place >> in function that uses count, it is constantly false if count is zero >> _and_ we have only one caller that passes non-zero. Moreover, in case of >> count being zero we ignore offset as well. >> >> Obvious things to do: >> a) take the check in question into the only caller that would >> pass non-zero count (i.e. rw_verify_area()) >> b) lose the second and the third arguments of __negative_fpos_check() >> c) sort out what the hell should be done in that place. >> >> Note that current logics is very convoluted. First of all, we rely on >> loff_t being long long in the kernel and size_t being not bigger than >> unsigned long long. See ((loff_t)(pos + count)< 0) in there - if count >> gets wider than pos, we suddenly get all kinds of odd crap. >> >> That's OK; if we run into ports with size_t wider than 64 bits, we'll have >> more trouble anyway. >> >> So what we have for signed offset case is pos>= 0, count<= max loff_t - pos. >> Fair enough. For unsigned offset case it's more interesting - we want to >> allow pos< 0, and we just want to prohibit wraparounds from negative to >> positive. IOW, it's pos>= 0 || count< -pos; note that we already have >> count<= max ssize_t, which we assume to be no more than max loff_t. >> >> So what we need is this: >> if (unlikely(pos< 0)) { >> if it's signed >> -EINVAL >> if (count>= -pos) /* both values are in 0..LLONG_MAX */ >> -EOVERFLOW >> } else if (unlikely((loff_t)(pos + count)< 0)) { >> if it's signed >> -EINVAL >> } >> and we are done with that. IOW, how about the patch below? >> >> >> Signed-off-by: Al Viro<viro@zeniv.linux.org.uk> > > This seems much easier to read. thank you. > > Acked-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@jp.fujitsu.com> > > and sorry for my 1st implemenation. > >> --- >> diff --git a/fs/read_write.c b/fs/read_write.c >> index 5d431ba..5520f8a 100644 >> --- a/fs/read_write.c >> +++ b/fs/read_write.c >> @@ -30,18 +30,9 @@ const struct file_operations generic_ro_fops = { >> >> EXPORT_SYMBOL(generic_ro_fops); >> >> -static int >> -__negative_fpos_check(struct file *file, loff_t pos, size_t count) >> +static inline int unsigned_offsets(struct file *file) >> { >> - /* >> - * pos or pos+count is negative here, check overflow. >> - * too big "count" will be caught in rw_verify_area(). >> - */ >> - if ((pos< 0)&& (pos + count< pos)) >> - return -EOVERFLOW; >> - if (file->f_mode& FMODE_UNSIGNED_OFFSET) >> - return 0; >> - return -EINVAL; >> + return file->f_mode& FMODE_UNSIGNED_OFFSET; >> } >> >> /** >> @@ -75,7 +66,7 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) >> break; >> } >> >> - if (offset< 0&& __negative_fpos_check(file, offset, 0)) >> + if (offset< 0&& !unsigned_offsets(file)) >> return -EINVAL; >> if (offset> inode->i_sb->s_maxbytes) >> return -EINVAL; >> @@ -152,7 +143,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) >> offset += file->f_pos; >> } >> retval = -EINVAL; >> - if (offset>= 0 || !__negative_fpos_check(file, offset, 0)) { >> + if (offset>= 0 || unsigned_offsets(file)) { >> if (offset != file->f_pos) { >> file->f_pos = offset; >> file->f_version = 0; >> @@ -252,9 +243,13 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count >> if (unlikely((ssize_t) count< 0)) >> return retval; >> pos = *ppos; >> - if (unlikely((pos< 0) || (loff_t) (pos + count)< 0)) { >> - retval = __negative_fpos_check(file, pos, count); >> - if (retval) >> + if (unlikely(pos< 0)) { >> + if (!unsigned_offsets(file)) >> + return retval; >> + if (count>= -pos) /* both values are in 0..LLONG_MAX */ Yeah .. thats a better reorg of code, however I am not sure if we need a typecast above for future portability reasons (as count and pos can be of diff width somewhere/sometime). Also, I personally would like to keep the function name as is_offset_unsigned(). Anyways, +1 for ACK ! Regards, Harsh >> + return -EOVERFLOW; >> + } else if (unlikely((loff_t) (pos + count)< 0)) { >> + if (!unsigned_offsets(file)) >> return retval; >> } >> >> > > -- > To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2010-12-19 7:02 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-12-08 12:55 [PATCH] Typecasting required for comparing unlike datatypes Harsh Prateek Bora 2010-12-09 18:32 ` Harsh Bora 2010-12-10 0:06 ` KAMEZAWA Hiroyuki 2010-12-10 0:53 ` KAMEZAWA Hiroyuki 2010-12-10 6:39 ` Harsh Bora 2010-12-10 7:01 ` KAMEZAWA Hiroyuki 2010-12-10 7:18 ` Harsh Bora 2010-12-10 7:59 ` KAMEZAWA Hiroyuki 2010-12-10 8:13 ` Harsh Bora 2010-12-10 8:20 ` KAMEZAWA Hiroyuki 2010-12-10 8:18 ` KAMEZAWA Hiroyuki 2010-12-10 8:31 ` Harsh Bora 2010-12-15 9:50 ` Al Viro 2010-12-16 0:24 ` KAMEZAWA Hiroyuki 2010-12-19 7:02 ` Harsh Bora
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).