changeset 27728:5e92bff668d6

disallow lvalue references in anonymous functions (bug #57255) * libinterp/parse-tree/anon-fcn-validator.h, libinterp/parse-tree/anon-fcn-validator.cc: New files. * module.mk: Update. * oct-parse.yy (anon_fcn_handle): Abort parse if make_anon_fcn_handle returns invalid object. (base_parser::make_anon_fcn_handle): Use new anon_fcn_validator class to check anonymous function. Set parser error and return nullptr if validation fails.
author John W. Eaton <jwe@octave.org>
date Wed, 20 Nov 2019 19:22:54 -0600
parents b8684580dd23
children 718845eb3c7a
files libinterp/parse-tree/anon-fcn-validator.cc libinterp/parse-tree/anon-fcn-validator.h libinterp/parse-tree/module.mk libinterp/parse-tree/oct-parse.yy
diffstat 4 files changed, 184 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/anon-fcn-validator.cc	Wed Nov 20 19:22:54 2019 -0600
@@ -0,0 +1,80 @@
+/*
+
+Copyright (C) 2019 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <string>
+
+#include "anon-fcn-validator.h"
+#include "ov.h"
+#include "pt-all.h"
+
+namespace octave
+{
+  anon_fcn_validator::anon_fcn_validator (tree_parameter_list *,
+                                          tree_expression *expr)
+    : m_ok (true), m_line (-1), m_column (-1), m_message ()
+  {
+    expr->accept (*this);
+  }
+
+  void anon_fcn_validator::visit_postfix_expression (tree_postfix_expression& expr)
+  {
+    octave_value::unary_op op = expr.op_type ();
+
+    if (op == octave_value::op_incr || op == octave_value::op_decr)
+      error (expr);
+    else
+      tree_walker::visit_postfix_expression (expr);
+  }
+
+  void anon_fcn_validator::visit_prefix_expression (tree_prefix_expression& expr)
+  {
+    octave_value::unary_op op = expr.op_type ();
+
+    if (op == octave_value::op_incr || op == octave_value::op_decr)
+      error (expr);
+    else
+      tree_walker::visit_prefix_expression (expr);
+  }
+
+  void anon_fcn_validator::visit_multi_assignment (tree_multi_assignment& expr)
+  {
+    error (expr);
+  }
+
+  void anon_fcn_validator::visit_simple_assignment (tree_simple_assignment& expr)
+  {
+    error (expr);
+  }
+
+  void anon_fcn_validator::error (tree_expression& expr)
+  {
+    m_ok = false;
+    m_line = expr.line ();
+    m_column = expr.column ();
+    m_message
+      = "invalid use of operator " + expr.oper () + " in anonymous function";
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/anon-fcn-validator.h	Wed Nov 20 19:22:54 2019 -0600
@@ -0,0 +1,79 @@
+/*
+
+Copyright (C) 2019 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_anon_fcn_validator_h)
+#define octave_anon_fcn_validator_h 1
+
+#include "octave-config.h"
+
+#include <string>
+
+#include "pt-walk.h"
+
+namespace octave
+{
+  class tree_expression;
+  class tree_parameter_list;
+
+  // How to check the semantics of the code that the parse trees represent.
+
+  class anon_fcn_validator : public tree_walker
+  {
+  public:
+
+    anon_fcn_validator (tree_parameter_list *, tree_expression *expr);
+
+    // No copying!
+
+    anon_fcn_validator (const anon_fcn_validator&) = delete;
+
+    anon_fcn_validator& operator = (const anon_fcn_validator&) = delete;
+
+    ~anon_fcn_validator (void) = default;
+
+    void visit_postfix_expression (tree_postfix_expression&);
+
+    void visit_prefix_expression (tree_prefix_expression&);
+
+    void visit_multi_assignment (tree_multi_assignment&);
+
+    void visit_simple_assignment (tree_simple_assignment&);
+
+    bool ok (void) const { return m_ok; }
+
+    int line (void) const { return m_line; }
+    int column (void) const { return m_column; }
+
+    std::string message (void) const { return m_message; }
+
+  private:
+
+    bool m_ok;
+    int m_line;
+    int m_column;
+    std::string m_message;
+
+    void error (tree_expression& expr);
+  };
+}
+
+#endif
--- a/libinterp/parse-tree/module.mk	Wed Nov 20 17:22:01 2019 -0800
+++ b/libinterp/parse-tree/module.mk	Wed Nov 20 19:22:54 2019 -0600
@@ -1,4 +1,5 @@
 PARSE_TREE_INC = \
+  %reldir%/anon-fcn-validator.h \
   %reldir%/bp-table.h \
   %reldir%/comment-list.h \
   %reldir%/jit-ir.h \
@@ -48,6 +49,7 @@
 ## be distributed but not installed.
 
 PARSE_TREE_SRC = \
+  %reldir%/anon-fcn-validator.cc \
   %reldir%/bp-table.cc \
   %reldir%/comment-list.cc \
   %reldir%/jit-ir.cc \
--- a/libinterp/parse-tree/oct-parse.yy	Wed Nov 20 17:22:01 2019 -0800
+++ b/libinterp/parse-tree/oct-parse.yy	Wed Nov 20 19:22:54 2019 -0600
@@ -53,6 +53,7 @@
 #include "quit.h"
 
 #include "Cell.h"
+#include "anon-fcn-validator.h"
 #include "builtin-defun-decls.h"
 #include "defun.h"
 #include "dynamic-ld.h"
@@ -606,6 +607,11 @@
 anon_fcn_handle : '@' param_list stmt_begin expr_no_assign
                   {
                     $$ = parser.make_anon_fcn_handle ($2, $4);
+                    if (! $$)
+                      {
+                        // make_anon_fcn_handle deleted $2 and $4.
+                        YYABORT;
+                      }
                     lexer.m_nesting_level.remove ();
                   }
                 | '@' param_list stmt_begin error
@@ -2393,6 +2399,23 @@
     int l = m_lexer.m_input_line_number;
     int c = m_lexer.m_current_input_column;
 
+    // FIXME: We need to examine EXPR and issue an error if any
+    // sub-expression contains an assignment, compound assignment,
+    // increment, or decrement operator.
+
+    anon_fcn_validator validator (param_list, expr);
+
+    if (! validator.ok ())
+      {
+        delete param_list;
+        delete expr;
+
+        bison_error (validator.message (), validator.line (),
+                     validator.column ());
+
+        return nullptr;
+      }
+
     symbol_scope fcn_scope = m_lexer.m_symtab_context.curr_scope ();
     symbol_scope parent_scope = m_lexer.m_symtab_context.parent_scope ();