Skip to contentSkip to conversation

let ERR_PTR BUILD_BUG_ON when we know its argument is not a valid errno

PATCH In review Marcin Slusarz 6 participants 21 messages May 11, 2008 – May 22, 2008

Subsystems (6)
Networking VFS NFS client NFS server +2 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 let ERR_PTR BUILD_BUG_ON when we know its argument is not a valid errno

---
allmodconfig compile tested (on x86_64)

should be applied after:
net/sunrpc/xprtrdma: fix svc_rdma_create out of memory error path
jfs: 0 is not valid errno value

---
 include/linux/err.h |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
include/linux/err.h +3 -1
@@ -19,11 +19,13 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)
+static inline void *__ERR_PTR(long error)
 {
 	return (void *) error;
 }
 
+#define ERR_PTR(error) (BUILD_BUG_ON(!IS_ERR_VALUE(error)), __ERR_PTR(error))
+
 static inline long PTR_ERR(const void *ptr)
 {
 	return (long) ptr;
#1 lore raw
Quoted 20 lines · On Sun, 11 May 2008 22:12:14 +0200 Marcin Slusarz <[email protected]>…

---
allmodconfig compile tested (on x86_64)

should be applied after:
net/sunrpc/xprtrdma: fix svc_rdma_create out of memory error path
jfs: 0 is not valid errno value

---
 include/linux/err.h |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
include/linux/err.h +0 -1
@@ -19,11 +19,13 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)

include/linux/err.h line 31

Quoted 10 lines
include/linux/err.h +3 -0
+static inline void *__ERR_PTR(long error)
 {
 	return (void *) error;
 }
 
+#define ERR_PTR(error) (BUILD_BUG_ON(!IS_ERR_VALUE(error)), __ERR_PTR(error))
+
 static inline long PTR_ERR(const void *ptr)
 {
 	return (long) ptr;

Not sure about this one. BUILD_BUG_ON only makes sense if the value is a compile-time constant. I think the code as you have it will take this:

	int e = foo();

	p = ERR_PTR(e);

and will attempt to evaluate sizeof() on a negative-sized array at runtime. The conmpile will laugh and throw that all away, but it's a bit weird.

Plus I'd have thought that the amount of code which does ERR_PTR(-EFOO) is fairly small, but perhaps that's wrong.

If I _am_ wrong then I do think it'd be saner to only do the BUILD_BUG_ON() if __builtin_constant_p(error) evaluates true. And even then I do think we'd like to see a more lengthy justification of why the kernel needs this check. More lengthy than zero, anyway...

(If a compile-time check is needed then why not a runtime one also?)

Thanks.

#2 lore raw
Quoted 41 lines · Andrew Morton
Quoted 20 lines · On Sun, 11 May 2008 22:12:14 +0200 Marcin Slusarz <[email protected]>…

---
allmodconfig compile tested (on x86_64)

should be applied after:
net/sunrpc/xprtrdma: fix svc_rdma_create out of memory error path
jfs: 0 is not valid errno value

---
 include/linux/err.h |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
include/linux/err.h +0 -1
@@ -19,11 +19,13 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)

include/linux/err.h line 31

Quoted 10 lines
include/linux/err.h +3 -0
+static inline void *__ERR_PTR(long error)
 {
 	return (void *) error;
 }
 
+#define ERR_PTR(error) (BUILD_BUG_ON(!IS_ERR_VALUE(error)), __ERR_PTR(error))
+
 static inline long PTR_ERR(const void *ptr)
 {
 	return (long) ptr;

Not sure about this one. BUILD_BUG_ON only makes sense if the value is a compile-time constant. I think the code as you have it will take this:

	int e = foo();

	p = ERR_PTR(e);
Quoted 6 lines

and will attempt to evaluate sizeof() on a negative-sized array at runtime. The conmpile will laugh and throw that all away, but it's a bit weird.

Plus I'd have thought that the amount of code which does ERR_PTR(-EFOO) is fairly small, but perhaps that's wrong.

$ git grep 'ERR_PTR(-E[A-Z]*)' | wc -l
1431
Quoted 2 lines

If I _am_ wrong then I do think it'd be saner to only do the BUILD_BUG_ON() if __builtin_constant_p(error) evaluates true. And even

I thought BUILD_BUG_ON uses __builtin_constant_p internally and it was a big mistake (see below).

Quoted 2 lines

then I do think we'd like to see a more lengthy justification of why the kernel needs this check.

Well, I think it's better to find more errors at compile time, than on rare runtime situation (error handling).
This patch found 2 errors on current sources (but one of them was harmless).

Quoted 3 lines

More lengthy than zero, anyway...

(If a compile-time check is needed then why not a runtime one also?)

I'm not sure - it would make kernel slightly bigger. I'll check that.

Today I discovered, that this patch causes funny runtime errors (/proc is mounted, but many applications think it's not), so ignore this patch for now. I'll prepare second version.

Marcin

#3 lore raw

cover Sanity checks for ERR_PTR argument

This little patchset adds sanity checks to ERR_PTR.

[PATCH 1/6] ERR_PTR: if errno value is known at compile time, make sure it's valid [PATCH 2/6] ERR_PTR: add ERR_OR_0_PTR
[PATCH 3/6] vfs: open_exec cleanup
[PATCH 4/6] procfs: switch ERR_PTR to ERR_OR_0_PTR when "error" might be 0 [PATCH 5/6] vfs: fix ERR_PTR abuse in generic_readlink
[PATCH 6/6] ERR_PTR: warn when ERR_PTR parameter is not errno value

The most important are patches 1 and 6. 2nd patch adds another ERR_PTR-like helper. The rest (3-5) fix false positives found by 6th patch.

---
 fs/exec.c           |   44 ++++++++++++++++++++++----------------------
 fs/namei.c          |   15 ++++++++-------
 fs/proc/base.c      |    4 ++--
 include/linux/err.h |   14 +++++++++++++-
 4 files changed, 45 insertions(+), 32 deletions(-)

---

#4 lore raw

PATCH 1/6 ERR_PTR: if errno value is known at compile time, make sure it's valid

In reply to Marcin Slusarz

ERR_PTR is easy to call with wrong argument (positive errno), and this error lead to catastrophic event - oops or kernel panic (dereference of invalid pointer).

As most of error handling code paths are rarely tested, this kind of bug can be hidden for years.

(Currently there are > 1400 calls of ERR_PTR with constant argument.)

---
 include/linux/err.h |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
include/linux/err.h +3 -1
index ec87f31..7b5daa6 100644
@@ -19,11 +19,13 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)
+static inline void *__ERR_PTR(long error)
 {
 	return (void *) error;
 }
 
+#define ERR_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && !IS_ERR_VALUE(error)), __ERR_PTR(error))
+
 static inline long PTR_ERR(const void *ptr)
 {
 	return (long) ptr;
Signature · 1 line
1.5.4.5
#5 lore raw

Re: [PATCH 1/6] ERR_PTR: if errno value is known at compile time, make sure it's valid

Quoted 12 lines · Marcin Slusarz

ERR_PTR is easy to call with wrong argument (positive errno), and this error lead to catastrophic event - oops or kernel panic (dereference of invalid pointer).

As most of error handling code paths are rarely tested, this kind of bug can be hidden for years.

(Currently there are > 1400 calls of ERR_PTR with constant argument.)

include/linux/err.h line 27

Quoted 10 lines
include/linux/err.h +2 -1
@@ -19,11 +19,13 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)
+static inline void *__ERR_PTR(long error)
 {
 	return (void *) error;
 }
 
+#define ERR_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && !IS_ERR_VALUE(error)), __ERR_PTR(error))

This needs comment that this construct must be a macro, otherwise gcc does something stupid and doesn't break the build (at least in the case below). BTW, I used grep(1) to find it.

[PATCH] xprtrdma: fix ERR_PTR(E typo

---

 net/sunrpc/xprtrdma/svc_rdma_transport.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +1 -1
@@ -661,7 +661,7 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
 
 	cma_xprt = rdma_create_xprt(serv, 1);
 	if (!cma_xprt)
-		return ERR_PTR(ENOMEM);
+		return ERR_PTR(-ENOMEM);
 	xprt = &cma_xprt->sc_xprt;
 
 	listen_id = rdma_create_id(rdma_listen_handler, cma_xprt, RDMA_PS_TCP);
#6 lore raw

Re: [PATCH 1/6] ERR_PTR: if errno value is known at compile time, make sure it's valid

Quoted 22 lines · Alexey Dobriyan
Quoted 21 lines · Marcin Slusarz

ERR_PTR is easy to call with wrong argument (positive errno), and this error lead to catastrophic event - oops or kernel panic (dereference of invalid pointer).

As most of error handling code paths are rarely tested, this kind of bug can be hidden for years.

(Currently there are > 1400 calls of ERR_PTR with constant argument.)

include/linux/err.h +1 -1
@@ -19,11 +19,13 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)
+static inline void *__ERR_PTR(long error)
 {
 	return (void *) error;
 }
 
Quoted 5 lines

include/linux/err.h line 27

Quoted 1 line
include/linux/err.h +1 -0
+#define ERR_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && !IS_ERR_VALUE(error)), __ERR_PTR(error))

This needs comment that this construct must be a macro, otherwise gcc does something stupid and doesn't break the build (at least in the case below).

Thanks, added to new version.

Quoted 1 line

BTW, I used grep(1) to find it.

http://lkml.org/lkml/2008/5/11/159

Quoted 21 lines

[PATCH] xprtrdma: fix ERR_PTR(E typo

---

 net/sunrpc/xprtrdma/svc_rdma_transport.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +1 -1
@@ -661,7 +661,7 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
 
 	cma_xprt = rdma_create_xprt(serv, 1);
 	if (!cma_xprt)
-		return ERR_PTR(ENOMEM);
+		return ERR_PTR(-ENOMEM);
 	xprt = &cma_xprt->sc_xprt;
 
 	listen_id = rdma_create_id(rdma_listen_handler, cma_xprt, RDMA_PS_TCP);
#7 lore raw

PATCH 2/6 ERR_PTR: add ERR_OR_0_PTR

In reply to Marcin Slusarz

Some codepaths call ERR_PTR with possibly 0 argument, which is not a valid errno and rely on conversion from 0 to NULL pointer. Add ERR_OR_0_PTR function which accepts errnos and 0 as an argument.

---
 include/linux/err.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)
include/linux/err.h +7 -0
index 7b5daa6..cdec8b6 100644
@@ -26,6 +26,13 @@ static inline void *__ERR_PTR(long error)
 
 #define ERR_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && !IS_ERR_VALUE(error)), __ERR_PTR(error))
 
+static inline void *__ERR_OR_0_PTR(long error)
+{
+	return (void *) error;
+}
+
+#define ERR_OR_0_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && error && !IS_ERR_VALUE(error)), __ERR_OR_0_PTR(error))
+
 static inline long PTR_ERR(const void *ptr)
 {
 	return (long) ptr;
Signature · 1 line
1.5.4.5
#8 lore raw

Re: [PATCH 2/6] ERR_PTR: add ERR_OR_0_PTR

Hi Marcin,

Quoted 15 lines · Marcin Slusarz

Some codepaths call ERR_PTR with possibly 0 argument, which is not a valid errno and rely on conversion from 0 to NULL pointer. Add ERR_OR_0_PTR function which accepts errnos and 0 as an argument.

---
 include/linux/err.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)
include/linux/err.h +0 -0
index 7b5daa6..cdec8b6 100644

include/linux/err.h static inline void *__ERR_PTR(long error) · line 34

Quoted 9 lines
include/linux/err.h +6 -0
@@ -26,6 +26,13 @@ static inline void *__ERR_PTR(long error)
 
 #define ERR_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && !IS_ERR_VALUE(error)), __ERR_PTR(error))
 
+static inline void *__ERR_OR_0_PTR(long error)
+{
+	return (void *) error;
+}
+
+#define ERR_OR_0_PTR(error) (BUILD_BUG_ON(__builtin_constant_p(error) && error && !IS_ERR_VALUE(error)), __ERR_OR_0_PTR(error))

ERR_OR_0_PTR could use __ERR_PTR() too.

And please break those lines ;)

Hannes

#9 lore raw

Re: [PATCH 2/6] ERR_PTR: add ERR_OR_0_PTR

In reply to Marcin Slusarz
Quoted 3 lines · Marcin Slusarz

Some codepaths call ERR_PTR with possibly 0 argument, which is not a valid errno and rely on conversion from 0 to NULL pointer. Add ERR_OR_0_PTR function which accepts errnos and 0 as an argument.

Sorry, no. ERR_PTR(0) is perfectly valid, you just don't want to return the actualy value. E.g. we have a common idiom of:

	some_ptr = ERR_PTR(err);
	if (IS_ERR(some_ptr))
		goto out_handle_err;

and obsfucating this with new syntactic sugar is not a good idea.

maintainer
#10 lore raw

Re: [PATCH 2/6] ERR_PTR: add ERR_OR_0_PTR

Quoted 5 lines · Christoph Hellwig
Quoted 3 lines · Marcin Slusarz

Some codepaths call ERR_PTR with possibly 0 argument, which is not a valid errno and rely on conversion from 0 to NULL pointer. Add ERR_OR_0_PTR function which accepts errnos and 0 as an argument.

Quoted 8 lines

Sorry, no. ERR_PTR(0) is perfectly valid, you just don't want to return the actualy value. E.g. we have a common idiom of:

	some_ptr = ERR_PTR(err);
	if (IS_ERR(some_ptr))
		goto out_handle_err;

and obsfucating this with new syntactic sugar is not a good idea.

Um... Could somebody explain WTF is wrong with declaring that ERR_PTR(0) is NULL? Sure, if we run into a target where converting non-constant integer with value zero to void * does not result in null pointer, we'll need to adjust ERR_PTR(). So. Fscking. What?

	a) it's not a lot of adjustment, to start with
	b) any such target will require much more work on porting anyway;

this part will be trivial.

#11 lore raw

PATCH 3/6 vfs: open_exec cleanup

In reply to Marcin Slusarz

open_exec is needlessly indented, calls ERR_PTR with 0 argument (which is not valid errno) and jumps into middle of function just to return value.
So clean it up a bit.

---
 fs/exec.c |   44 ++++++++++++++++++++++----------------------
 1 files changed, 22 insertions(+), 22 deletions(-)
fs/exec.c +22 -22
index aeaa979..ca8b512 100644
@@ -656,34 +656,34 @@ struct file *open_exec(const char *name)
 	struct nameidata nd;
 	int err;
 	struct file *file;
+	struct inode *inode;
 
 	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
-	file = ERR_PTR(err);
-
-	if (!err) {
-		struct inode *inode = nd.path.dentry->d_inode;
-		file = ERR_PTR(-EACCES);
-		if (S_ISREG(inode->i_mode)) {
-			int err = vfs_permission(&nd, MAY_EXEC);
-			file = ERR_PTR(err);
-			if (!err) {
-				file = nameidata_to_filp(&nd,
-							O_RDONLY|O_LARGEFILE);
-				if (!IS_ERR(file)) {
-					err = deny_write_access(file);
-					if (err) {
-						fput(file);
-						file = ERR_PTR(err);
-					}
+	if (err)
+		return ERR_PTR(err);
+
+	inode = nd.path.dentry->d_inode;
+	file = ERR_PTR(-EACCES);
+	if (S_ISREG(inode->i_mode)) {
+		int err = vfs_permission(&nd, MAY_EXEC);
+		if (!err) {
+			file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
+			if (!IS_ERR(file)) {
+				err = deny_write_access(file);
+				if (err) {
+					fput(file);
+					file = ERR_PTR(err);
 				}
-out:
-				return file;
 			}
+			goto out;
 		}
-		release_open_intent(&nd);
-		path_put(&nd.path);
+		else
+			file = ERR_PTR(err);
 	}
-	goto out;
+	release_open_intent(&nd);
+	path_put(&nd.path);
+out:
+	return file;
 }
 
 EXPORT_SYMBOL(open_exec);
Signature · 1 line
1.5.4.5
#12 lore raw

Re: [PATCH 3/6] vfs: open_exec cleanup

Quoted 4 lines · Marcin Slusarz

open_exec is needlessly indented, calls ERR_PTR with 0 argument (which is not valid errno) and jumps into middle of function just to return value.
So clean it up a bit.

Still looks rather messy. See below for a better version.

fs/exec.c +29 -27
Index: linux-2.6/fs/exec.c
===================================================================
@@ -654,38 +654,40 @@ EXPORT_SYMBOL(setup_arg_pages);
 struct file *open_exec(const char *name)
 {
 	struct nameidata nd;
-	int err;
 	struct file *file;
+	int err;
 
-	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
-	file = ERR_PTR(err);
+	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd,
+				FMODE_READ|FMODE_EXEC);
+	if (err)
+		goto out;
 
-	if (!err) {
-		struct inode *inode = nd.path.dentry->d_inode;
-		file = ERR_PTR(-EACCES);
-		if (S_ISREG(inode->i_mode)) {
-			int err = vfs_permission(&nd, MAY_EXEC);
-			file = ERR_PTR(err);
-			if (!err) {
-				file = nameidata_to_filp(&nd,
-							O_RDONLY|O_LARGEFILE);
-				if (!IS_ERR(file)) {
-					err = deny_write_access(file);
-					if (err) {
-						fput(file);
-						file = ERR_PTR(err);
-					}
-				}
-out:
-				return file;
-			}
-		}
-		release_open_intent(&nd);
-		path_put(&nd.path);
+	err = -EACCES;
+	if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
+		goto out_path_put;
+
+	err = vfs_permission(&nd, MAY_EXEC);
+	if (err)
+		goto out_path_put;
+
+	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
+	if (IS_ERR(file))
+		return file;
+
+	err = deny_write_access(file);
+	if (err) {
+		fput(file);
+		goto out;
 	}
-	goto out;
-}
 
+	return file;
+
+ out_path_put:
+	release_open_intent(&nd);
+	path_put(&nd.path);
+ out:
+	return ERR_PTR(err);
+}
 EXPORT_SYMBOL(open_exec);
 
 int kernel_read(struct file *file, unsigned long offset,
author Reviewed-by
#13 lore raw

Re: [PATCH 3/6] vfs: open_exec cleanup

Quoted 7 lines · Christoph Hellwig
Quoted 4 lines · Marcin Slusarz

open_exec is needlessly indented, calls ERR_PTR with 0 argument (which is not valid errno) and jumps into middle of function just to return value.
So clean it up a bit.

Still looks rather messy. See below for a better version.

looks much better, thanks

Quoted 76 lines

Index: linux-2.6/fs/exec.c
===================================================================

fs/exec.c +29 -27
@@ -654,38 +654,40 @@ EXPORT_SYMBOL(setup_arg_pages);
 struct file *open_exec(const char *name)
 {
 	struct nameidata nd;
-	int err;
 	struct file *file;
+	int err;
 
-	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
-	file = ERR_PTR(err);
+	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd,
+				FMODE_READ|FMODE_EXEC);
+	if (err)
+		goto out;
 
-	if (!err) {
-		struct inode *inode = nd.path.dentry->d_inode;
-		file = ERR_PTR(-EACCES);
-		if (S_ISREG(inode->i_mode)) {
-			int err = vfs_permission(&nd, MAY_EXEC);
-			file = ERR_PTR(err);
-			if (!err) {
-				file = nameidata_to_filp(&nd,
-							O_RDONLY|O_LARGEFILE);
-				if (!IS_ERR(file)) {
-					err = deny_write_access(file);
-					if (err) {
-						fput(file);
-						file = ERR_PTR(err);
-					}
-				}
-out:
-				return file;
-			}
-		}
-		release_open_intent(&nd);
-		path_put(&nd.path);
+	err = -EACCES;
+	if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
+		goto out_path_put;
+
+	err = vfs_permission(&nd, MAY_EXEC);
+	if (err)
+		goto out_path_put;
+
+	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
+	if (IS_ERR(file))
+		return file;
+
+	err = deny_write_access(file);
+	if (err) {
+		fput(file);
+		goto out;
 	}
-	goto out;
-}
 
+	return file;
+
+ out_path_put:
+	release_open_intent(&nd);
+	path_put(&nd.path);
+ out:
+	return ERR_PTR(err);
+}
 EXPORT_SYMBOL(open_exec);
 
 int kernel_read(struct file *file, unsigned long offset,
#14 lore raw

PATCH 4/6 procfs: switch ERR_PTR to ERR_OR_0_PTR when "error" might be 0

In reply to Marcin Slusarz

proc_pid_follow_link and proc_self_follow_link call ERR_PTR with 0 argument (invalid errno) in most of situtations.
Replace ERR_PTR calls with ERR_OR_0_PTR which accepts 0 argument.

---
 fs/proc/base.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
fs/proc/base.c +2 -2
index 808cbdc..ebbb8e6 100644
@@ -1292,7 +1292,7 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
 	error = PROC_I(inode)->op.proc_get_link(inode, &nd->path);
 	nd->last_type = LAST_BIND;
 out:
-	return ERR_PTR(error);
+	return ERR_OR_0_PTR(error);
 }
 
 static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
@@ -2228,7 +2228,7 @@ static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
 	if (!tgid)
 		return ERR_PTR(-ENOENT);
 	sprintf(tmp, "%d", task_tgid_nr_ns(current, ns));
-	return ERR_PTR(vfs_follow_link(nd,tmp));
+	return ERR_OR_0_PTR(vfs_follow_link(nd,tmp));
 }
 
 static const struct inode_operations proc_self_inode_operations = {
Signature · 1 line
1.5.4.5
#15 lore raw

PATCH 5/6 vfs: fix ERR_PTR abuse in generic_readlink

In reply to Marcin Slusarz

generic_readlink calls ERR_PTR for negative and positive values (vfs_readlink returns length of "link"), but it should not (not an errno) and does not need to.

Cc Al Viro <[email protected]>
Cc Andrew Morton <[email protected]>
Cc Christoph Hellwig <[email protected]>
---
 fs/namei.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)
fs/namei.c +8 -7
index 32fd965..959523d 100644
@@ -2853,16 +2853,17 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
 	struct nameidata nd;
 	void *cookie;
+	int res;
 
 	nd.depth = 0;
 	cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
-	if (!IS_ERR(cookie)) {
-		int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
-		if (dentry->d_inode->i_op->put_link)
-			dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
-		cookie = ERR_PTR(res);
-	}
-	return PTR_ERR(cookie);
+	if (IS_ERR(cookie))
+		return PTR_ERR(cookie);
+
+	res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
+	if (dentry->d_inode->i_op->put_link)
+		dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
+	return res;
 }
 
 int vfs_follow_link(struct nameidata *nd, const char *link)
Signature · 1 line
1.5.4.5
#16 lore raw

PATCH 6/6 ERR_PTR: warn when ERR_PTR parameter is not errno value

In reply to Marcin Slusarz

Check at runtime whether error argument of ERR_PTR and ERR_OR_0_PTR is valid. It can catch bugs which possibly lead to oops or panic earlier.

Currently there are > 600 calls of ERR_PTR with non-constant argument in Linux kernel sources.

---
 include/linux/err.h |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
include/linux/err.h +3 -0
index cdec8b6..51e2459 100644
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 
+#include <asm/bug.h>
 #include <asm/errno.h>
 
 /*
@@ -21,6 +22,7 @@
 
 static inline void *__ERR_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error));
 	return (void *) error;
 }
 
@@ -28,6 +30,7 @@ static inline void *__ERR_PTR(long error)
 
 static inline void *__ERR_OR_0_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error) && error);
 	return (void *) error;
 }
 
Signature · 1 line
1.5.4.5
#17 lore raw

Re: [PATCH 6/6] ERR_PTR: warn when ERR_PTR parameter is not errno value

Hi Marcin,

Quoted 33 lines · Marcin Slusarz

Check at runtime whether error argument of ERR_PTR and ERR_OR_0_PTR is valid. It can catch bugs which possibly lead to oops or panic earlier.

Currently there are > 600 calls of ERR_PTR with non-constant argument in Linux kernel sources.

---
 include/linux/err.h |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
include/linux/err.h +2 -0
index cdec8b6..51e2459 100644
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 
+#include <asm/bug.h>
 #include <asm/errno.h>
 
 /*
@@ -21,6 +22,7 @@
 
 static inline void *__ERR_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error));
 	return (void *) error;
 }
 

include/linux/err.h static inline void *__ERR_PTR(long error) · line 35

Quoted 6 lines
include/linux/err.h +1 -0
@@ -28,6 +30,7 @@ static inline void *__ERR_PTR(long error)
 
 static inline void *__ERR_OR_0_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error) && error);
 	return (void *) error;
 }

How about WARN_ON_ONCE() instead? That would warn once for each erroneous user which should be enough.

Hannes

#18 lore raw

Re: [PATCH 6/6] ERR_PTR: warn when ERR_PTR parameter is not errno value

Quoted 28 lines · Johannes Weiner
Quoted 26 lines · Marcin Slusarz

Check at runtime whether error argument of ERR_PTR and ERR_OR_0_PTR is valid. It can catch bugs which possibly lead to oops or panic earlier.

Currently there are > 600 calls of ERR_PTR with non-constant argument in Linux kernel sources.

include/linux/err.h +2 -0
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 
+#include <asm/bug.h>
 #include <asm/errno.h>
 
 /*
@@ -21,6 +22,7 @@
 
 static inline void *__ERR_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error));
 	return (void *) error;
 }
 
@@ -28,6 +30,7 @@ static inline void *__ERR_PTR(long error)
 
Quoted 8 lines

include/linux/err.h static inline void *__ERR_PTR(long error) · line 35

Quoted 5 lines
include/linux/err.h +1 -0
 static inline void *__ERR_OR_0_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error) && error);
 	return (void *) error;
 }

How about WARN_ON_ONCE() instead? That would warn once for each erroneous user which should be enough.

And blow up .bss ?

#19 lore raw

Re: [PATCH 6/6] ERR_PTR: warn when ERR_PTR parameter is not errno value

Hi,

Quoted 38 lines · Alexey Dobriyan
Quoted 28 lines · Johannes Weiner
Quoted 26 lines · Marcin Slusarz

Check at runtime whether error argument of ERR_PTR and ERR_OR_0_PTR is valid. It can catch bugs which possibly lead to oops or panic earlier.

Currently there are > 600 calls of ERR_PTR with non-constant argument in Linux kernel sources.

include/linux/err.h +2 -0
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 
+#include <asm/bug.h>
 #include <asm/errno.h>
 
 /*
@@ -21,6 +22,7 @@
 
 static inline void *__ERR_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error));
 	return (void *) error;
 }
 
@@ -28,6 +30,7 @@ static inline void *__ERR_PTR(long error)
 
Quoted 8 lines

include/linux/err.h static inline void *__ERR_PTR(long error) · line 35

Quoted 5 lines
include/linux/err.h +1 -0
 static inline void *__ERR_OR_0_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error) && error);
 	return (void *) error;
 }

How about WARN_ON_ONCE() instead? That would warn once for each erroneous user which should be enough.

Quoted 1 line

And blow up .bss ?

Well, okay. Perhaps ratelimiting? I really prefer a bigger image size over a spammed dmesg.

Hannes

#20 lore raw

Re: [PATCH 6/6] ERR_PTR: warn when ERR_PTR parameter is not errno value

Quoted 42 lines · Johannes Weiner

Hi,

Quoted 38 lines · Alexey Dobriyan
Quoted 36 lines · Johannes Weiner
Quoted 25 lines · Marcin Slusarz

Check at runtime whether error argument of ERR_PTR and ERR_OR_0_PTR is valid. It can catch bugs which possibly lead to oops or panic earlier.

Currently there are > 600 calls of ERR_PTR with non-constant argument in Linux kernel sources.

include/linux/err.h +2 -0
@@ -3,6 +3,7 @@
 
 #include <linux/compiler.h>
 
+#include <asm/bug.h>
 #include <asm/errno.h>
 
 /*
@@ -21,6 +22,7 @@
 
 static inline void *__ERR_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error));
 	return (void *) error;
 }
 

include/linux/err.h static inline void *__ERR_PTR(long error) · line 35

Quoted 6 lines
include/linux/err.h +1 -0
@@ -28,6 +30,7 @@ static inline void *__ERR_PTR(long error)
 
 static inline void *__ERR_OR_0_PTR(long error)
 {
+	WARN_ON(!IS_ERR_VALUE(error) && error);
 	return (void *) error;
 }

How about WARN_ON_ONCE() instead? That would warn once for each erroneous user which should be enough.

Quoted 4 lines
Quoted 1 line

And blow up .bss ?

Well, okay. Perhaps ratelimiting? I really prefer a bigger image size over a spammed dmesg.

Well, when I add 0 as a valid argument for ERR_PTR, this check won't add too many false positives, so I think simple WARN_ON is safe.

Marcin