Chapel: Segmentation Fault when attempting to write to file without file permissions

Created on 9 Jul 2018  路  12Comments  路  Source: chapel-lang/chapel

I noticed that when I attempted to open a file with bad permissions, such as one that is owned by root, it will cause a runtime crash/segmentation fault, a very confusing one at that. Would it be possible to add a runtime check to determine if opening a file has failed, and if so throw a runtime error that is a bit more helpful, such as open: "Bad file permissions for file %FILENAME%".

Compiler Libraries / Modules Runtime Bug user issue

All 12 comments

Can you show more about the error? E.g. example code, steps to reproduce?

I tried to reproduce:

proc test() {
  var f = open("testfile.txt", iomode.rw);
}
test();
$ ls -l testfile.txt
-rw------- 1 root user 0 Jul  9 14:59 testfile.txt

running the program results in:

uncaught PermissionError: Permission denied (in open with path "testfile.txt")
  x.chpl:2: thrown here
  x.chpl:2: uncaught here

Segmentation Fault

 proc badFn(fileName) throws {
   var badFile = open(fileName, iomode.cw).writer();
 }
 badFn("out.dot");

Output

*** Caught a fatal signal: SIGSEGV(11) on node 0/1
[0] 0: ./BadFilePermissionMWE_real() [0x4b6c3f] gasneti_bt_execinfo gasnet_tools.c:?
[0] 1: ./BadFilePermissionMWE_real() [0x4bc7ff] gasneti_print_backtrace ??:?
[0] 2: ./BadFilePermissionMWE_real() [0x52d98f] gasneti_defaultSignalHandler ??:?
[0] 3: /lib/x86_64-linux-gnu/libc.so.6(+0x354b0) [0x7f1b5cb854b0] ?? ??:0
[0] 4: ./BadFilePermissionMWE_real() [0x5392eb] _ZL20AMUDP_RequestGeneric16amudp_category_tP8amudp_epjhPvimiP13__va_list_taghh.constprop.7 amudp_reqrep.cpp:?
[0] 5: ./BadFilePermissionMWE_real() [0x539c16] AMUDP_RequestIVA ??:?
[0] 6: ./BadFilePermissionMWE_real() [0x4b186a] gasnetc_AMRequestMediumM ??:?
[0] 7: ./BadFilePermissionMWE_real() [0x48a83e] chpl_comm_execute_on ??:?
[0] 8: ./BadFilePermissionMWE_real() [0x452f3b] chpl_executeOn /home/LouisJenkinsCS/chgl/src/modules/LocaleModelHelpFlat.chpl:55
[0] 9: ./BadFilePermissionMWE_real() [0x46b8df] deinit_chpl3 /home/LouisJenkinsCS/chgl/src/modules/IO.chpl:2010 (discriminator 2)
[0] 10: ./BadFilePermissionMWE_real() [0x4588c8] badFn_chpl /home/LouisJenkinsCS/chgl/src/modules/BadFilePermissionMWE.chpl:2 (discriminator 7)
[0] 11: ./BadFilePermissionMWE_real() [0x4583eb] chpl__init_BadFilePermissionMWE /home/LouisJenkinsCS/chgl/src/modules/BadFilePermissionMWE.chpl:4 (discriminator 1)
[0] 12: ./BadFilePermissionMWE_real() [0x458d07] chpl_gen_main /home/LouisJenkinsCS/chgl/src/modules/BadFilePermissionMWE.chpl:1
[0] 13: ./BadFilePermissionMWE_real() [0x480288] chpl_executable_init ??:?
[0] 14: ./BadFilePermissionMWE_real() [0x4856e1] main_wrapper tasks-qthreads.c:?
[0] 15: ./BadFilePermissionMWE_real() [0x545da8] qthread_wrapper qthread.c:?

About

Compilation command: chpl BadFilePermissionMWE.chpl --devel -g --cpp-lines
Chapel compiler version: 1.18.0 pre-release (2bcd25f)
Chapel environment:
  CHPL_HOME: /home/LouisJenkinsCS/chapel
  CHPL_ATOMICS: intrinsics
  CHPL_AUX_FILESYS: none
  CHPL_COMM: gasnet
  CHPL_COMM_SUBSTRATE: udp
  CHPL_COMPILER_SUBDIR: linux64/gnu/llvm-none
  CHPL_GASNET_SEGMENT: everything
  CHPL_GMP: gmp
  CHPL_GMP_UNIQ_CFG_PATH: linux64-gnu-unknown
  CHPL_HOST_COMPILER: gnu
  CHPL_HOST_MEM: cstdlib
  CHPL_HOST_PLATFORM: linux64
  CHPL_HWLOC: hwloc
  CHPL_HWLOC_UNIQ_CFG_PATH: linux64-gnu-unknown-flat
  CHPL_JEMALLOC: jemalloc
  CHPL_JEMALLOC_UNIQ_CFG_PATH: linux64-gnu-unknown
  CHPL_LAUNCHER: amudprun
  CHPL_LAUNCHER_SUBDIR: linux64/gnu/loc-flat/comm-gasnet/udp/everything/tasks-qthreads/launch-amudprun/tmr-generic/unwind-none/mem-jemalloc/atomics-intrinsics
  CHPL_LIBUNWIND_UNIQ_CFG_PATH: linux64-gnu-unknown
  CHPL_LLVM: none
  CHPL_LOCALE_MODEL: flat
  CHPL_MAKE: make
  CHPL_MASSIVETHREADS_UNIQ_CFG_PATH: linux64-gnu-unknown
  CHPL_MEM: jemalloc
  CHPL_NETWORK_ATOMICS: none
  CHPL_ORIG_TARGET_COMPILER: gnu
  CHPL_QTHREAD_UNIQ_CFG_PATH: linux64-gnu-unknown-flat
  CHPL_RE2_UNIQ_CFG_PATH: linux64-gnu-unknown
  CHPL_REGEXP: re2
  CHPL_RUNTIME_ARCH: unknown
  CHPL_RUNTIME_INCL: /home/LouisJenkinsCS/chapel/runtime/include
  CHPL_RUNTIME_LIB: /home/LouisJenkinsCS/chapel/lib
  CHPL_RUNTIME_SUBDIR: linux64/gnu/arch-unknown/loc-flat/comm-gasnet/udp/everything/tasks-qthreads/tmr-generic/unwind-none/mem-jemalloc/atomics-intrinsics/hwloc/re2/fs-none
  CHPL_TARGET_ARCH: unknown
  CHPL_TARGET_ARCH_FLAG: none
  CHPL_TARGET_BACKEND_ARCH: unknown
  CHPL_TARGET_COMPILER: gnu
  CHPL_TARGET_MEM: jemalloc
  CHPL_TARGET_PLATFORM: linux64
  CHPL_TASKS: qthreads
  CHPL_THIRD_PARTY: /home/LouisJenkinsCS/chapel/third-party
  CHPL_THIRD_PARTY_LINK_ARGS: -lgmp -lchpl -lqthread -L/home/LouisJenkinsCS/chapel/third-party/hwloc/install/linux64-gnu-unknown-flat/lib -L/home/LouisJenkinsCS/chapel/third-party/
jemalloc/install/linux64-gnu-unknown/lib -ljemalloc -lhwloc -lm -lre2 -lpthread
  CHPL_TIMERS: generic
  CHPL_UNWIND: none

Although, I am getting weird behavior while experimenting with this... Like, sometimes I saw it spin indefinitely, other times I immediately saw a GASNet active message error.

Disregard the above, turns out it was mixing and matching older (likely cached) files with new ones causing the interesting behavior. I still get a segfault but not with the above example.

Try this one?

 proc badFn(unusedObj, fileName = "out.dot") throws {
   var f = open(fileName, iomode.cw).writer();

   proc innerFn(v, ref str) {
     f.writeln(v);
   }

   f.writeln("}");
   f.close();
 }

 proc main() {
   badFn(new object());
 }

And you can generate file of course via sudo touch out.dot

Also note that if you pass badFn(1) instead of new object() I get the throw error message. Its almost like undefined behavior. For example, try this...

 proc badFn(unusedObj, fileName = "out.dot") throws {
   var f = open(fileName, iomode.cw).writer();

   proc innerFn(v, ref str) {
     f.writeln(v);
   }

   f.writeln("}");
   f.close();
 }

 record R {var x = 1; var y = 2; var z = 3; var w = 4; }
 proc main() {
   var r : R;
   badFn(r);
 }

The output I get is...

AMUDP int sendPacket(ep_t, amudp_msg_t*, size_t, en_t, packet_type) returning an error code: AM_ERR_RESOURCE (Problem with requested resource)
  from function sendPacket
  at /home/LouisJenkinsCS/chapel/third-party/gasnet/gasnet-src/other/amudp/amudp_reqrep.cpp:113
  reason: Address family not supported by protocol
AMUDP int AMUDP_RequestGeneric(amudp_category_t, ep_t, amudp_node_t, handler_t, void*, int, uintptr_t, int, __va_list_tag*, uint8_t, uint8_t) returning an error code: AM_ERR_RESOURCE (Problem with requested resource)
  at /home/LouisJenkinsCS/chapel/third-party/gasnet/gasnet-src/other/amudp/amudp_reqrep.cpp:1107

GASNet gasnetc_AMRequestMediumM encountered an AM Error: AM_ERR_RESOURCE(3)
  at /home/LouisJenkinsCS/chapel/third-party/gasnet/gasnet-src/udp-conduit/gasnet_core.c:701
GASNet gasnetc_AMRequestMediumM returning an error code: GASNET_ERR_RESOURCE (Problem with requested resource)
  at /home/LouisJenkinsCS/chapel/third-party/gasnet/gasnet-src/udp-conduit/gasnet_core.c:705
ERROR calling: gasnet_AMRequestMedium0(node, op, f, small_msg_size)
 at: comm-gasnet.c:1548
 error: GASNET_ERR_RESOURCE (Problem with requested resource)
config var oneline = true;

proc badFn(fileName) throws {
  if (oneline) {
    var badFile = open(fileName, iomode.cw).writer();
  } else {
    var badFile = open(fileName, iomode.cw);
    var w = badFile.writer();
  }
 }
badFn("out.dot");
fortytwo@magrathea:~/src/chapel (master)$ ./badfn -nl 1 --oneline=false
uncaught PermissionError: Permission denied (in open with path "out.dot")
  $CHPL_HOME/modules/standard/SysError.chpl:411: thrown here
  badfn.chpl:11: uncaught here
fortytwo@magrathea:~/src/chapel (master)$ ./badfn -nl 1 --oneline=true
*** Caught a fatal signal: SIGSEGV(11) on node 0/1
NOTICE: Before reporting bugs, run with GASNET_BACKTRACE=1 in the environment to generate a backtrace. 

Thank you @cassella, its good to confirm that its not just my configuration/setup. @mppf I believe this needs to be flagged as a Compiler issue as well.

I get the same behavior with a file-not-found by changing the open to use iomode.r, and the writer() to reader(). It's the same behavior, but simpler to run the test without having to create a file as root.

If I change the oneline version to var badFile = try! open(...).reader(), it behaves as expected,

fortytwo@magrathea:~/src/chapel (master)$ ./badfn -nl 1 --oneline=true
uncaught FileNotFoundError: No such file or directory (in open with path "oot.doot")
  badfn.chpl:5: thrown here
  badfn.chpl:5: uncaught here

I forget the ramifications of the various error-handling strictness modes, but I think there's really supposed to be a try around every function call that can throw, even if the function it's in is marked throws.

I'm putting this in the backlog under the assumption that it might be a quick fix. If that's not the case, I'm very open to moving it to the icebox.

The problem isn't specific to the IO module:

record R {
  var s = "world";
  proc hello() { writeln("Hello ", s); return this; }

  proc init() { writeln("in init"); }
  proc deinit() { writeln("in deinit"); }
}

proc gimmeanr(): R throws {
  var ret: R;
  throw new Error();
  return ret;
}

proc intro() throws {
  var r = gimmeanr().hello();
}

intro();
fortytwo@magrathea:~/src/chapel (master)$ ./foo -nl 1
in init
in deinit
in deinit
*** Caught a fatal signal: SIGSEGV(11) on node 0/1

More calls to deinit than to init seems fishy to me.

In a valgrind-compatible CHPL_COMM=none build,

...
==7084== 
in init
in deinit
in deinit
==7084== Thread 2:
==7084== Conditional jump or move depends on uninitialised value(s)
==7084==    at 0x40E6C3: deinit16 (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x42D874: deinit_chpl6 (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x42DA32: intro_chpl (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x42D3F1: chpl__init_foo (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x42D5B4: chpl_gen_main (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x442517: chpl_executable_init (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x445EA9: do_callMain (in /home/fortytwo/src/chapel-none/foo)
==7084==    by 0x4E416B9: start_thread (pthread_create.c:333)
==7084==    by 0x546741C: clone (clone.S:109)
...
uncaught Error: 
  foo.chpl:11: thrown here
  foo.chpl:19: uncaught here

deinit16 seems to be string.deinit().
deinit_chpl6 is my R.deinit().

The problem is in the generated code for intro:

static void intro_chpl(Error * error_out_chpl) {
  R_chpl r_chpl;
  R_chpl call_tmp_chpl;
  Error error_chpl = NULL;
  R_chpl ret_tmp_chpl;
  chpl_bool errorExists_chpl;
  _ref_R i_x_chpl = NULL;
  error_chpl = nil;
  gimmeanr_chpl(&error_chpl, &ret_tmp_chpl);
  call_tmp_chpl = ret_tmp_chpl;
  errorExists_chpl = (error_chpl != nil);
  if (errorExists_chpl) {
    *(error_out_chpl) = error_chpl;
    i_x_chpl = &r_chpl;
    deinit_chpl7(i_x_chpl); // r_chpl was never initialized, so we shouldn't deinit it
    goto _endintro_chpl;
  }
Was this page helpful?
0 / 5 - 0 ratings