1

My program calls exec() on a binary that has been written to a directory specified by the user. If the directory resides in a tree mounted with "noexec", the exec() fails with EACCES.

Instead of failing the exec(), I would like to be able to figure out if a directory was mounted with noexec, but none of fcntl(), stat() or mount() return this info (from reading the manpages). Looking at the kernel source for the exec system call, it looks like this info is stored in the metadata of the inode, and I don't see this info being returned from any system call.

 673     struct nameidata nd;
 (..)
 677     err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
 678     file = ERR_PTR(err);
 (..)
 682         file = ERR_PTR(-EACCES);
 683         if (!(nd.mnt->mnt_flags & MNT_NOEXEC) &&
 684             S_ISREG(inode->i_mode)) {

Does anyone know of a way to do this?

Thanks.

Mayank
  • 1,236
  • 1
  • 12
  • 22
  • 2
    What's wrong with checking if exec fails with EACCES? – Jesus Ramos Mar 29 '13 at 20:48
  • 2
    Indeed, attempting to check if an operation will fail before performing it, instead of trying it and checking for failure, is **almost always a bug**. – R.. GitHub STOP HELPING ICE Mar 29 '13 at 20:56
  • @JesusRamos: Nothing wrong with that, I want to flag an error earlier. – Mayank Mar 29 '13 at 21:20
  • @R..: *In general* it would be a bad idea, but in my program it won't matter, and will make it easier for the user. – Mayank Mar 29 '13 at 21:21
  • @Mayank How does it make it easier or better for the user? `if (can_exec) report_err(); else exec();` vs `if (exec) report_err();` Which one seems "easier" to you. – Jesus Ramos Mar 29 '13 at 21:22
  • Another thing to keep in mind is that failure is the exception, not the norm. So it's better to try and fail than to check each time before you try (at least in this case). And to elaborate more on R's point remember that EACCESS is not the only reason that an exec can fail so you would need even more checks and it's not always fool proof (OS can OOM and there's no easy way to check for that). – Jesus Ramos Mar 29 '13 at 21:24
  • @JesusRamos: My app sets the directory up from one interactive process and execs a binary from another noninteractive process. Where I do the error check matters. That's why it's "easier". – Mayank Mar 29 '13 at 22:35
  • @Mayank So you want to check for every possible error? You can use basic IPC to solve this. – Jesus Ramos Mar 29 '13 at 22:38

2 Answers2

2

You can use statvfs()

struct statvfs st;
inr rc = statvfs("/mnt/foo", &st);
if (rc == -1)
   error();
if (st.f_flag & ST_NOEXEC) {
 //no exec flags was set
}
nos
  • 223,662
  • 58
  • 417
  • 506
  • Thanks, this does the job, but is defeated if "/mnt/foo/bar" is passed as an argument to statvfs. f_flags doesn't have ST_NOEXEC set, only /mnt/foo does. – Mayank Mar 29 '13 at 23:28
  • I need to make my code work on Mac too so looking at /proc as suggested in http://stackoverflow.com/questions/2337139/where-is-a-file-mounted?rq=1 isn't an option. – Mayank Mar 29 '13 at 23:36
  • Bah, I don't like the other option of using realpath() + stat() to compare devices either. Never mind. – Mayank Mar 29 '13 at 23:40
  • @Mayank I cannot reproduce that here. You should be able to give any path below the mount point as an argument and receive the correct mount flags for that file system. – nos Mar 29 '13 at 23:45
  • I can see the difference pretty consistently on kernel 2.6.35.8. – Mayank Mar 29 '13 at 23:57
0

Did you consider reading /proc/mounts then determining which file system your program binary resides on, perhaps using statfs(2) and/or realpath(3)?

But you always should care about, and handle, the failure of execve(2) and related exec functions, which can fail for a variety of reasons.

There are many ways execve can fail, and some of them are not easily reproducible; likewise for the failure of fork or any other syscall.

I would either leave and report the EACCESS error (of execve), or, if you absolutely want to catch & explain more the noexec mount option, do the more complex thing (statfs and/or realpath and scanning of /proc/mounts or /proc/self/mounts) after such an EACCESS failure.

There is no much point in testing a binary path before execve-ing it ... just report the error after...

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547