Description
sassc-ruby
supports passing custom functions to libsass
as ruby functions, for example it is used in sassc-rails
to define url
. Generally, this mechanism is memory-safe, but if custom function raises an exception that is not a subclass of StandardError
, such as SystemStackError
, segmentation fault happens.
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/engine.rb:43: [BUG] Segmentation fault at 0x0000000000000000
ruby 3.3.3 (2024-06-12 revision f1c7b6f435) +YJIT [x86_64-linux]
-- Control frame information -----------------------------------------------
c:0179 p:---- s:1241 e:001240 CFUNC :compile_data_context
c:0178 p:0209 s:1236 E:001140 METHOD /home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/engine.rb:43
-- C level backtrace information -------------------------------------------
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(rb_print_backtrace+0x14) [0x70428cf2b89b] /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/vm_dump.c:820
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(rb_vm_bugreport) /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/vm_dump.c:1151
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(rb_bug_for_fatal_signal+0x100) [0x70428cd18ae0] /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/error.c:1065
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(sigsegv+0x4b) [0x70428ce70a3b] /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/signal.c:926
/usr/lib/libc.so.6(0x70428c88dae0) [0x70428c88dae0]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(sass_value_get_tag+0xc) [0x70426fe9a736]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass4EvalclEPNS_13Function_CallE+0x1de0) [0x70426fd84936]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass13Function_Call7performEPNS_9OperationIPNS_10ExpressionEEE+0x30) [0x70426fd103fa]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass6ExpandclEPNS_10AssignmentE+0xa23) [0x70426fda033d]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass10Assignment7performEPNS_9OperationIPNS_9StatementEEE+0x2e) [0x70426fcb66b0]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass6Expand12append_blockEPNS_5BlockE+0xce) [0x70426fda6a9a]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass6ExpandclEPNS_5BlockE+0x15d) [0x70426fd9d1d7]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass7Context7compileEv+0x2cf) [0x70426fd44a53]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(_ZN4Sass12Data_Context5parseEv+0x47d) [0x70426fd445bb]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(0x70426fe96a3b) [0x70426fe96a3b]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(sass_compiler_parse+0xb2) [0x70426fe977af]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(0x70426fe96faa) [0x70426fe96faa]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/sassc-2.4.0/lib/sassc/libsass.so(sass_compile_data_context+0xc3) [0x70426fe97503]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/ffi-1.17.0-x86_64-linux-gnu/lib/3.3/ffi_c.so(0x7042700f9052) [0x7042700f9052]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/ffi-1.17.0-x86_64-linux-gnu/lib/3.3/ffi_c.so(0x7042700f84a3) [0x7042700f84a3]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/ffi-1.17.0-x86_64-linux-gnu/lib/3.3/ffi_c.so(0x7042700f861e) [0x7042700f861e]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/ffi-1.17.0-x86_64-linux-gnu/lib/3.3/ffi_c.so(rbffi_CallFunction+0x110) [0x7042700ec210]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/ffi-1.17.0-x86_64-linux-gnu/lib/3.3/ffi_c.so(0x7042700efe2d) [0x7042700efe2d]
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(vm_cfp_consistent_p+0x0) [0x70428cefc911] /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/vm_insnhelper.c:3490
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(vm_call_cfunc_with_frame_) /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/vm_insnhelper.c:3492
/home/kolen/.asdf/installs/ruby/3.3.3/lib/libruby.so.3.3(vm_call_cfunc_with_frame) /tmp/ruby-build.20240624142351.32349.tRcN10/ruby-3.3.3/vm_insnhelper.c:3518
This example causes segmentation fault by causing custom function to raise SystemStackError
by infinite recursive call:
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'sassc', '2.4.0'
end
require 'sassc'
stylesheet = '$demo: demo();'
module DemoFunctions
def demo
demo
end
end
SassC::Engine.new(stylesheet, syntax: :scss, functions: DemoFunctions).render
Function wrapper catches only StandardError
. If non-StandardError
exception occurs, ffi
gem ignores exception in FFI::Function
's block and behaves like the function is returned nil
.
sassc-ruby/lib/sassc/functions_handler.rb
Lines 22 to 33 in 4fce2b6
libsass
requires return value from custom function to be a valid pointer to Sass_Value
. This return value is then passed to sass_value_get_tag
, which tries to dereference null pointer.
https://github.com/sass/libsass/blob/8d312a1c91bb7dd22883ebdfc829003f75a82396/src/eval.cpp#L1107-L1108
union Sass_Value* c_val = c_func(c_args, c_function, compiler());
if (sass_value_get_tag(c_val) == SASS_ERROR) {
enum Sass_Tag ADDCALL sass_value_get_tag(const union Sass_Value* v) { return v->unknown.tag; }
This might be cause of #133, #197, #207 (unsure).
Maybe this can be fixed by just catching all exceptions:
--- a/lib/sassc/functions_handler.rb 2024-07-01 17:12:43.522095555 +0300
+++ b/lib/sassc/functions_handler.rb 2024-07-01 16:37:31.255145300 +0300
@@ -25,7 +25,7 @@
function_arguments = arguments_from_native_list(native_argument_list)
result = functions_wrapper.send(custom_function, *function_arguments)
to_native_value(result)
- rescue StandardError => exception
+ rescue Exception => exception
# This rescues any exceptions that occur either in value conversion
# or during the execution of a custom function.
error(exception.message)
It works for my case, catching "stack level too deep" and converting to sass compilation error, but I'm not sure if it's overall a good idea to catch non-StandardError
exceptions and what consequences might it cause, especially when working near ffi boundaries. Maybe calling Kernel#abort
in case of non-StandardError
is a better idea.
Activity