Safe analysis of format directives in general Lisp

I would like to read a line from the input file (which may or may not have been changed by the user). I would like to consider this line as a format directive that needs to be called with a fixed number of arguments. However, I understand that some format directives (in particular, ~/ come to mind) could potentially be used to enter function calls, making this approach inherently unsafe.

When using read to parse data in Common Lisp, the language provides a dynamic variable *read-eval* that can be set to nil to disable #. code injection. I am looking for something similar that would prevent code injection and calls to arbitrary functions inside format directives.

+5
source share
2 answers

If the user cannot enter user code, but only format the lines, then you can avoid print-object problems. Remember to use with-standard-io-syntax (or its customized version) to control the exact type of output you will create (think of *print-base* , ...).

You can scan the input lines to detect the presence of ~/ (but ~~/ ) and refuse to interpret the format that contains blacklists. However, some analyzes are more complicated, and you may need to act at runtime.

For example, if the format string is incorrect, you will probably get an error that needs to be handled (you can also give bad values โ€‹โ€‹to the expected arguments).

Even if the user is not malicious, you may also have problems with iteration constructs:

 ~{<X>~:*~} 

... never stops because ~:* rewinds the current argument. To deal with this, you must consider that <X> may or may not print anything. You can implement both of these strategies:

  • has a timeout to limit time formatting.
  • if the record is too large (for example, writing to a string buffer), the main stream reaches the end of the file.

There may be other problems that I donโ€™t see right now, be careful.

+4
source

It's not just ~ / that you will need to worry about. The pretty pretty printer functionality has many possibilities for extending code, and even ~ A can cause problems, because objects can have methods for print-objects . For instance,

 (defclass x () ()) (defmethod print-object ((xx) stream) (format *error-output* "Executing arbitrary code...~%") (call-next-method x stream)) 

 CL-USER> (format t "~A" (make-instance 'x)) Executing arbitrary code... #<X {1004E4B513}> NIL 

I think you need to determine for yourself which directives are safe, using any criteria that you consider important, and then include only those.

+3
source

All Articles