Skip to contentSkip to conversation

Behaviour of access(x, X_OK) in 2.2 vs. 2.4

DISCUSSION Peter Backes 1 participant 2 messages Jul 2, 2003 – Jul 3, 2003

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

Hi,

I'm still using linux 2.2 and I noticed today that the behaviour of the access() system call concerning the execution permission (X_OK), if invoked by uid 0, has been changed in 2.4. In 2.4 it seems to take the execute permission bit into account while in 2.2, for uid 0, it returns success (0) independent from it, although execve fails if invoked on a file without x bit. The difference can be demonstrated quite easily using the bash builtin test command on a file without x bit, which (like /usr/bin/access from tetex and unlike /usr/bin/test from sh-utils) seems to use access():

On 2.2:
bash# cd /tmp && touch xx && test -x xx && echo x || echo y x

although

bash# cd /tmp && cp /bin/echo . && chmod 0 echo && ./echo
bash: ./echo: Permission denied

On 2.4:
bash# cd /tmp && touch xx && test -x xx && echo x || echo y y

(Note this assumes an umask of 0022.)

I searched the web, newsgroups and mailing list archives about this problem, to no avail. Is there some backport, workaround or patch for 2.2 to get the same (and obsiously more sane) behaviour as in 2.4?

Please make sure you CC me if you reply as I'm not subscribed. -- Peter 'Rattacresh' Backes, [email protected]

#1 lore raw

PATCH (2.2): Fix for misbehaving access(x, X_OK)

Hi,

here is a patch for the quirk in 2.2 kernels I reported yesterday. I mostly took the changes from 2.4.9 to 2.4.10, see
http://lists.insecure.org/lists/linux-kernel/2001/Sep/0152.html,
and applied them to 2.2.25. Thus it is based on the patch by Christoph Hellwig.

Keywords:
access() system call, X_OK, sys_access(), permission(),
vfs_permission(), execute, x-bit, root, uid 0, bash, test -x,
/usr/bin/access -x, CAP_DAC_OVERRIDE,
/usr/lib/rpm/find-requires: /usr/lib/rpm/perl.req: /usr/bin/perl: bad interpreter: Permission denied

Please make sure you CC me if you reply as I'm not subscribed.

-- Peter 'Rattacresh' Backes, [email protected]

fs/namei.c renamed from fs/namei.c.old +16 -4
@@ -149,11 +149,23 @@
 		mode >>= 6;
 	else if (in_group_p(inode->i_gid))
 		mode >>= 3;
-	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
 		return 0;
-	/* read and search access */
-	if ((mask == S_IROTH) ||
-	    (S_ISDIR(inode->i_mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
 		if (capable(CAP_DAC_READ_SEARCH))
 			return 0;
 	return -EACCES;
fs/ext2/acl.c renamed from fs/ext2/acl.c.old +16 -3
@@ -51,10 +51,23 @@
 	 * Access is always granted for root. We now check last,
          * though, for BSD process accounting correctness
 	 */
-	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
 		return 0;
-	if ((mask == S_IROTH) ||
-	    (S_ISDIR(inode->i_mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
 		if (capable(CAP_DAC_READ_SEARCH))
 			return 0;
 	return -EACCES;
fs/ufs/acl.c renamed from fs/ufs/acl.c.old +16 -3
@@ -58,10 +58,23 @@
 	 * Access is always granted for root. We now check last,
 	 * though, for BSD process accounting correctness
 	 */
-	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
 		return 0;
-	if ((mask == S_IROTH) ||
-	    (S_ISDIR(inode->i_mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
 		if (capable(CAP_DAC_READ_SEARCH))
 			return 0;
 	return -EACCES;
fs/proc/inode.c renamed from fs/proc/inode.c.old +16 -4
@@ -145,11 +145,23 @@
 		mode >>= 6;
 	else if (in_group_p(inode->i_gid))
 		mode >>= 3;
-	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
+	/*
+	 * If the DACs are ok we don't need any capability check.
+	 */
+	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
 		return 0;
-	/* read and search access */
-	if ((mask == S_IROTH) ||
-	    (S_ISDIR(inode->i_mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+	/*
+	 * Read/write DACs are always overridable.
+	 * Executable DACs are overridable if at least one exec bit is set.
+	 */
+	if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+		if (capable(CAP_DAC_OVERRIDE))
+			return 0;
+
+	/*
+	 * Searching includes executable on directories, else just read.
+	 */
+	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
 		if (capable(CAP_DAC_READ_SEARCH))
 			return 0;
 	return -EACCES;