Почему именно зло?

140
задан Jay 27 November 2017 в 05:11
поделиться

7 ответов

Как "правило" GOTO: если вы не знаете, что делаете, вы можете устроить беспорядок.

Помимо создания чего-то только из известных и безопасных данных, существует проблема, заключающаяся в том, что некоторые языки / реализации не могут достаточно оптимизировать код. Вы можете получить интерпретируемый код внутри eval .

4
ответ дан 23 November 2019 в 23:10
поделиться

Eval просто небезопасен. Например, у вас есть следующий код:

eval('
hello('.$_GET['user'].');
');

Теперь пользователь заходит на ваш сайт и вводит URL http://example.com/file.php?user=);$is_admin=true;echo(

Тогда результирующий код будет:

hello();$is_admin=true;echo();
4
ответ дан 23 November 2019 в 23:10
поделиться

eval (на любом языке) не является злом, как бензопила - не злом. Это инструмент. Это мощный инструмент, который при неправильном использовании может отрезать конечности и выпотрошить (образно говоря), но то же самое можно сказать и о многих инструментах в наборе инструментов программиста, включая:

  • goto и друзья
  • lock на основе потоков
  • продолжения
  • макросов (гигиенических или других)
  • указателей
  • перезапускаемых исключений
  • самомодифицируемого кода
  • ... и нескольких тысяч.

Если вам приходится использовать любой из этих мощных, потенциально опасных инструментов, трижды спросите себя: «Почему?» в цепочке. Например:

«Почему я должен использовать eval ?» "Из-за foo." "Зачем нужен foo ?" «Потому что ...»

Если вы дойдете до конца этой цепочки, а инструмент по-прежнему выглядит правильным, то сделайте это. Задокументируйте ад из него. Испытайте ад из него. Перепроверяйте правильность и безопасность снова и снова. Но сделай это.

41
ответ дан 23 November 2019 в 23:10
поделиться

IMO, этот вопрос не относится к LISP . Вот ответ на тот же вопрос для PHP, и он применим к LISP, Ruby и другим другим языкам, в которых есть eval:

Основные проблемы с eval ():

  • Потенциально небезопасный ввод. Передача ненадежного параметра приводит к ошибке . Часто бывает нетривиальной задачей убедиться, что параметру (или его части ) полностью доверяют.
  • Хитрость. Использование eval () делает код умным, поэтому усложняет выполнение кода. Цитируя Брайана Кернигана " Отладка вдвое сложнее, чем написание кода с самого начала. Следовательно, если вы пишете код как насколько это возможно, вы, по определению , недостаточно умен, чтобы отлаживать его "

Основная проблема с фактическим использованием eval () заключается только в one:

  • неопытные разработчики, которые используют его без должного внимания.

Взято из здесь .

Я думаю, что хитрость - это замечательный момент. Одержимость кодом для гольфа и кратким кодом всегда приводила к «умному» коду (для которого evals - отличный инструмент). Но вы должны писать свой код для удобства чтения, IMO, а не для демонстрации того, что вы сообразительный, и , чтобы не экономить бумагу (вы все равно не будете его печатать).

Затем в LISP возникает некоторая проблема, связанная с контекстом, в котором выполняется eval, поэтому ненадежный код может получить доступ к большему количеству вещей; в любом случае эта проблема кажется распространенной.

14
ответ дан 23 November 2019 в 23:10
поделиться

Eval в порядке, если вы ТОЧНО знаете, что в нем происходит. Любой ввод пользователя в него ДОЛЖЕН быть проверен и подтвержден, и все такое. Если вы не знаете, как быть уверенным на 100%, не делайте этого.

По сути, пользователь может ввести любой код для рассматриваемого языка, и он будет выполнен. Вы можете себе представить, какой ущерб он может нанести.

26
ответ дан 23 November 2019 в 23:10
поделиться

"Когда мне следует использовать eval ?" может быть лучший вопрос.

Короткий ответ: «когда ваша программа предназначена для написания другой программы во время выполнения, а затем для ее выполнения». Генетическое программирование является примером ситуации, в которой, вероятно, имеет смысл использовать eval .

21
ответ дан 23 November 2019 в 23:10
поделиться

Существует несколько причин, по которым не следует использовать EVAL.

Основная причина для новичков: вам это не нужно.

Пример (предполагая Common Lisp):

EVALuate выражение с разными операторами:

(let ((ops '(+ *)))
  (dolist (op ops)
    (print (eval (list op 1 2 3)))))

Это лучше записать как:

(let ((ops '(+ *)))
  (dolist (op ops)
    (print (funcall op 1 2 3))))

Есть много примеров, когда начинающие изучающие Lisp думают, что им нужен EVAL, но им это не нужно - так как выражения вычисляются и можно также оценить функциональную часть. В большинстве случаев использование EVAL показывает непонимание оценщика.

Та же проблема с макросами. Часто новички пишут макросы, где они должны писать функции — не понимая, для чего на самом деле предназначены макросы и не понимая, что функция уже выполняет свою работу.

Часто это неправильный инструмент для работы, чтобы использовать EVAL , и это часто указывает на то, что новичок не понимает обычных правил оценки Lisp.

Если вы считаете, что вам нужен EVAL, затем проверьте, можно ли использовать вместо этого что-то вроде FUNCALL, REDUCE или APPLY.

  • FUNCALL - вызов функции с аргументами: (funcall '+ 1 2 3)
  • REDUCE - вызов функции по списку значений и объединение результатов: (reduce '+ '(1 2 3))
  • APPLY - вызов функции со списком в качестве аргументов: (apply '+ '(1 2 3)).

В: Действительно ли мне нужен eval или компилятор/оценщик уже то, что я действительно хочу?

Основные причины избегать EVAL для немного более продвинутых пользователей:

  • вы хотите убедиться, что ваш код скомпилирован, потому что компилятор может проверять код на наличие многих проблем и генерирует более быстрый код, иногда НАМНОГО МНОГО (это фактор 1000 ;-) ) ) более быстрый код

  • код, который построен и нуждается в оценке, не может быть скомпилирован как можно раньше.

  • Eval произвольного пользовательского ввода открывает проблемы безопасности

  • Некоторое использование оценки с EVAL может произойти в неподходящее время и создать проблемы сборки

Чтобы объяснить последний пункт с помощью упрощенного примера:

(defmacro foo (a b)
  (list (if (eql a 3) 'sin 'cos) b))

Итак, я могу написать макрос, который на основе первого параметра использует либо SIN, либо COS.

(фу 3 4) делает (грех 4) и (foo 1 4) делает (cos 4).

Теперь мы можем иметь:

(foo (+ 2 1) 4)

Это не дает желаемого результата.

Затем можно восстановить макрос FOO путем EVALuating переменной:

(defmacro foo (a b)
  (list (if (eql (eval a) 3) 'sin 'cos) b))

(foo (+ 2 1) 4)

Но тогда это все равно не работает:

(defun bar (a b)
  (foo a b))

Значение переменной просто неизвестно во время компиляции.

Общая важная причина избегать EVAL: он часто используется для уродливых взломов.

148
ответ дан 23 November 2019 в 23:10
поделиться