bugGNU Octave - Bugs: bug #50068, Segfault, when eval_string is used...

 
 

bug #50068: Segfault, when eval_string is used in interpreter embedded mode

Submitted by:  Dmitry Messerman <mdmitry>
Submitted on:  Tue 17 Jan 2017 07:27:47 PM UTC  
 
Category:  Interpreter Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Segfault, Bus Error, etc.
Status:  Fixed Assigned to:  None
Originator Name:  Dmitry Messerman Open/Closed:  Closed
Release:  4.2.0 Operating System:  GNU/Linux

Add a New Comment (Rich Markup)
   

You are not logged in

Please log in, so followups can be emailed to you.

 

( Jump to the original submission)

Sun 29 Jan 2017 02:33:16 PM UTC, comment #21: 

Octave in an all-volunteer effort.  Because of that, there is no guarantee that any particular bug report will be investigated, and if it is investigated, no guarantee that it will be resolved or on what kind of schedule it might get reviewed.

If you do need certainty, consider either 1) paid support such as that listed at https://www.gnu.org/software/octave/commercial-support.html, or 2) performing your own investigations since 100% of the source code is available through the Mercurial repository.

Rik <rik5>
Project Administrator
Sun 29 Jan 2017 12:33:15 PM UTC, comment #20: 

Several days ago, I've posted another related bug:
https://savannah.gnu.org/bugs/?50111

However, nobody from Octave team have looked at it so far.
Please, let me know, when I should expect any kind of response.

Thanks,
  Dmitry

Dmitry Messerman <mdmitry>
Fri 27 Jan 2017 07:18:26 PM UTC, comment #19: 

I'll update the example, then close.

John W. Eaton <jwe>
Project Administrator
Fri 27 Jan 2017 05:22:45 PM UTC, comment #18: 

Is this bug ready to be closed now?  Or should there be an update to the embedded.cc example on stable so people don't attempt this?

Rik <rik5>
Project Administrator
Tue 24 Jan 2017 11:40:03 PM UTC, comment #17: 

I checked in the following changeset on default

http://hg.savannah.gnu.org/hgweb/octave/rev/9f406f0b36da

I don't know if there is a fix that can be applied to stable.

Note that this deprecates both octave_main and clean_up_and_exit.  I'll try to fix the docs and example.

The best thing to do is to create your own embedded_application object and let it go out of scope when you are done (or delete it if you allocated it with new).

However, I'm not sure you can reliably create another one after doing that since there are still many global objects that may expect to only be initialized once.

John W. Eaton <jwe>
Project Administrator
Sat 21 Jan 2017 10:20:11 PM UTC, comment #16: 

Is there a documentation fix we can make today on the stable branch as a placeholder before any other changes?  The only indication of how to use the embedded interpreter occurs in an example in Appendix A of the manual.  The actual code, that is sucked in to the manual, is located in examples/code/embedded.cc.  Should that be rewritten to follow jwe's code in comment #14?

Rik <rik5>
Project Administrator
Sat 21 Jan 2017 08:08:12 AM UTC, comment #15: 

Thanks for proposed solution!
It works fine for me.
With regards to clean_up_and_exit: it is not essential for me.
I guess it will be fixed in one of the new versions.

Dmitry Messerman <mdmitry>
Fri 20 Jan 2017 10:17:25 PM UTC, comment #14: 

We can fix the problem with the interpreter object being temporary in octave_main by making it static.  But I'm not sure what to do about the new way that clean_up_and_exit works.

If you want to update your code, you could write something like this instead:

#include <string>
#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
#include <octave/interpreter.h>

int
main (int argc, char** argv)
{
  string_vector octave_argv (2);
  octave_argv(0) = "embedded";
  octave_argv(1) = "-q";
  octave::embedded_application app (2, octave_argv.c_str_vec ());

  if (! app.execute ())
    {
      std::cerr << "creating embedded Octave interpreter failed!" << std::endl;
      exit (1);
    }

  try
    {
      int parse_status;

      eval_string (std::string ("x = 2;"), 0, parse_status);
      eval_string (std::string ("printf ('X = %d\\n', x);"), 0, parse_status);
    }
  catch (const octave::exit_exception& ex)
    {
      exit (ex.exit_status ());
    }
  catch (const octave::execution_exception&)
    {
      std::cerr << "error evaluating Octave code!" << std::endl;
    }

  return 0;
}

John W. Eaton <jwe>
Project Administrator
Wed 18 Jan 2017 06:04:07 PM UTC, comment #13: 

One more comment.
With regards to the Rik's response.

1) Vanilla code from Appendix A does not reproduce problem, which is reported in the ticket, as feval works fine both in 4.0.2 and 4.2.0

In order to reproduce problem, which is is reported in the ticket, I'm aware of 2 ways:

- Use eval_string: example is attached in original submission
- Use source_file: if needed, I can create example of this one
  as well

2) Exception thrown by cleanup_and_exit is another issue.
   I've not reported it, as it has trivial workaround:
   use try - catch brackets.

Dmitry Messerman <mdmitry>
Wed 18 Jan 2017 06:03:57 PM UTC, comment #12: 

Oh, I was just looking at the example provided by Rik.  I didn't read enough context here...

I'll try to take a look at the issue that Mike brought up about the interperter being destroyed when octave_main returns.

John W. Eaton <jwe>
Project Administrator
Wed 18 Jan 2017 06:01:14 PM UTC, comment #11: 

It looks like a case of missing a catch() for the exit exception.

Until it's fixed in Octave, you should be able to work around it with something like this:

  int status = 0;  // Or whatever status you want to return.

  try
    {
      clean_up_and_exit (status);
    }
  catch (const octave::exit_exception&)
    {
      // clean_up_and_exit should have done this for us...
      exit (status);
    }

John W. Eaton <jwe>
Project Administrator
Wed 18 Jan 2017 05:55:34 PM UTC, comment #10: 

Thanks for clarification!
Now I understand.
I guess that I need to wait for the next release.

Dmitry Messerman <mdmitry>
Wed 18 Jan 2017 05:48:27 PM UTC, comment #9: 

> I've checked the hypothesis that octave_main does not change internal state of the process after exit.


That was not a hypothesis I put forward. What I said was that the interpreter object is initialized and then destroyed by the time octave_main returns. I did not say that that is the only thing that octave_main does. It definitely affects global state as well. Changes were made between 4.0 and 4.2 to try to encapsulate that state better into classes, and this crash appears to be a side effect that the work is still in progress.

Mike Miller <mtmiller>
Project Administrator
Wed 18 Jan 2017 05:35:05 PM UTC, comment #8: 

Please note that the error you are seeing with clean_up_and_exit is not the same as the issue reported here.

The functions feval and eval_string appear to rely on different parts of the interpreter. For example, I don't think feval needs the parser to be active at all, since you are passing it a function name and an argument list, you are in effect acting as the parser.

Calling eval_string passes the text down to the parser, and that is where the crash reported here is happening.

Mike Miller <mtmiller>
Project Administrator
Wed 18 Jan 2017 04:55:41 PM UTC, comment #7: 

Confirmed.  This is a regression between 4.0.2 and 4.2.0.  The problem is still present on the development branch (4.3.0+) as well.  I'm adding jwe to the CC list since he significantly changed the exception handling mechanisms of Octave to which this appears to relate.

For a test case, I am using the vanilla code present in Appendix A, shown here for reference.

#include <iostream>
#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
#include <octave/interpreter.h>

int
main (void)
{
  string_vector argv (2);
  argv(0) = "embedded";
  argv(1) = "-q";

  octave_main (2, argv.c_str_vec (), 1);

  octave_idx_type n = 2;
  octave_value_list in;

  for (octave_idx_type i = 0; i < n; i++)
    in(i) = octave_value (5 * (i + 2));

  octave_value_list out = feval ("gcd", in, 1);

  if (out.length () > 0)
  std::cout << "GCD of ["
            << in(0).int_value ()
            << ", "
            << in(1).int_value ()
            << "] is " << out(0).int_value ()
            << std::endl;
  else
    std::cout << "invalid\n";

   clean_up_and_exit (0);
}

To compile, I type at a shell prompt

mkoctfile-4.2.0 --link-stand-alone tst.cc -o tst

Also, at the shell, I have to add in the runtime libraries

setenv LD_LIBRARY_PATH /usr/local/lib/octave/4.2.0

Finally, I run the resulting executable and I get

GCD of [10, 15] is 5
terminate called after throwing an instance of 'octave::exit_exception'
Abort

It seems that clean_up_and_exit() behaves differently than it did in 4.0.2.  If I eliminate the call to clean_up_and_exit and just let the interpreter go out of scope at the end of main() theni I get

GCD of [10, 15] is 5
Inconsistency detected by ld.so: dl-close.c: 764: _dl_close: Assertion `map->l_init_called' failed!

Rik <rik5>
Project Administrator
Wed 18 Jan 2017 10:09:26 AM UTC, comment #6: 

I've checked the hypothesis that octave_main does not change internal state of the process after exit.
According to my understanding based on the following test is doing something.

Reproduction:
1) The following code works well

#include <iostream>
#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
#include <octave/interpreter.h>

int
main (void)
{
  string_vector argv (2);
  argv(0) = "embedded";
  argv(1) = "-q";

  octave_main (2, argv.c_str_vec (), 1);

  octave_idx_type n = 2;
  octave_value_list in;

  for (octave_idx_type i = 0; i < n; i++)
    in(i) = octave_value (5 * (i + 2));

  octave_value_list out = feval ("gcd", in, 1);

  if (out.length () > 0)
    std::cout << "GCD of ["
              << in(0).int_value ()
              << ", "
              << in(1).int_value ()
              << "] is " << out(0).int_value ()
              << std::endl;
  else
    std::cout << "invalid\n";

  try {
      clean_up_and_exit (0);
  } catch(...) {
  }
}

2) The same code with line

octave_main (2, argv.c_str_vec (), 1);

commented out is crashing.

I don't understand, how above can happen if octave_main only doing the following:

1) Creates interpreter object
2) Destroys interpreter object

Dmitry Messerman <mdmitry>
Wed 18 Jan 2017 08:37:45 AM UTC, comment #5: 

Thanks for quick response!
Any plans to fix this issue?

Dmitry Messerman <mdmitry>
Wed 18 Jan 2017 07:38:01 AM UTC, comment #4: 

It seems that when octave_main returns, the interpreter object has been destroyed. It has gone through its command line processing and initialization of its internal state, but then it exits and cleans itself up. The subsequent call to eval_string is trying to operate on an interpreter that doesn't exist anymore. This was not the case in Octave 4.0.x, because the interpreter was not an object with contained state, it was just initializing a bunch of globals.

Mike Miller <mtmiller>
Project Administrator
Wed 18 Jan 2017 07:27:32 AM UTC, comment #3: 

I agree that these examples, and my own tests, modified from the provided embedded.cc boilerplate example, succeed with Octave 4.0.x but fail on the default branch. I don't have a working 4.2 to test at the moment, but I expect it is similar to default at this point.

The segfault occurs at

Thread 1 "embedded" received signal SIGSEGV, Segmentation fault.
0x00007ffff6e69aea in octave::application::interactive () at ../libinterp/octave.cc:362
362            interpreter *interp = instance->m_interpreter;
(gdb) bt
#0  0x00007ffff6e69aea in octave::application::interactive () at ../libinterp/octave.cc:362
#1  0x00007ffff739f22b in octave::base_lexer::reset (this=0x555555dc4bc0) at ../libinterp/parse-tree/lex.ll:2327
#2  0x00007ffff73ad1c4 in octave::base_parser::reset (this=this@entry=0x7fffffffd750) at libinterp/parse-tree/oct-parse.yy:2161
#3  0x00007ffff73b77bf in eval_string (eval_str="who", silent=silent@entry=false, parse_status=@0x7fffffffd9ac: 0, nargout=nargout@entry=1) at libinterp/parse-tree/oct-parse.yy:5058
#4  0x00007ffff73b7d40 in eval_string (eval_str="who", silent=<optimized out>, parse_status=@0x7fffffffd9ac: 0) at libinterp/parse-tree/oct-parse.yy:5121
#5  0x00005555555563a8 in main () at embedded.cc:23

Mike Miller <mtmiller>
Project Administrator
Wed 18 Jan 2017 06:58:31 AM UTC, comment #2: 

Answering your questions:

1) Very similar code worked for 4.0.2
   I've changed only header from toplev.h to interpreter.h
   Note that on 4.2.0 version with toplev.h segfaults as well
2) There are other scenarios, where feval is not applicable.
   Attached below is example of more generic case, which works
   on 4.0.2 and segfaults on 4.2.0

#include <string>
#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
#include <octave/toplev.h>

int main (int argc, char** argv) {
  string_vector octave_argv (2);
  octave_argv(0) = "embedded";
  octave_argv(1) = "-q";
  octave_main (2, octave_argv.c_str_vec (), 1);
  int parse_status;
  eval_string(std::string("[2,1;1,2]\\[1;1]"),0, parse_status);
  std::_Exit(0);
  return 0;
}

Expected output from 4.0.2:

ans =

   0.333333333333333
   0.333333333333333

Current output from 4.2.0:

Segmentation fault

Dmitry Messerman <mdmitry>
Wed 18 Jan 2017 04:59:35 AM UTC, comment #1: 

Has this code worked with any version of Octave?  Why not use feval if you want to evaluate a function as shown in Appendix A of the Octave manual?

Rik <rik5>
Project Administrator
Tue 17 Jan 2017 07:27:47 PM UTC, original submission:  

The following code segfaults:

#include <string>
#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
#include <octave/interpreter.h>

int main (int argc, char** argv) {
  string_vector octave_argv (2);
  octave_argv(0) = "embedded";
  octave_argv(1) = "-q";
  octave_main (2, octave_argv.c_str_vec (), 1);
  int parse_status;
  eval_string(std::string("who"),0, parse_status);
  return 0;
}

Dmitry Messerman <mdmitry>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by jwe (Posted a comment)
  • -email is unavailable- added by rik5
  • -email is unavailable- added by rik5 (Posted a comment)
  • -email is unavailable- added by mdmitry (Submitted the item)
  •  

    Do you think this task is very important?
    If so, you can add your encouragement to it.
    This task has 0 encouragements so far.

    Only project members can vote.

     

     

     

    Follow 6 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2019-03-03 mtmiller Carbon-CopyRemoved 80942 => -
    2017-03-13 rik5 StatusConfirmed => Fixed
        Open/ClosedOpen => Closed
    2017-01-18 rik5 StatusNeed Info => Confirmed
        Carbon-Copy- => Added jwe
    2017-01-18 rik5 StatusNone => Need Info

    Back to the top


    Powered by Savane 3.5