found while hunting #1153738: Filter settings being ignored :

* create a filter module, in hook_filter_info() define 'process callback'=>'my_process_callback'
* accidentally let the function my_process_callback() undefined (removed include, typo,...)

result:
* filter is silently ignored

expected:
* filter system should yell to watchdog and display 'broken filter' instead of passing unfiltered content

CommentFileSizeAuthor
#11 drupal-1154254-11.patch1.27 KBavr
#6 filter.patch868 bytescatch

Comments

geek-merlin’s picture

see:
http://api.drupal.org/api/drupal/modules--filter--filter.module/function...


  // Perform filtering.
  foreach ($filters as $name => $filter) {
    if ($filter->status && isset($filter_info[$name]['process callback']) && function_exists($filter_info[$name]['process callback'])) {
      $function = $filter_info[$name]['process callback'];
      $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
    }
  }


catch’s picture

Title: not-defined filter process callback leads to unfiltered content » Missing filter process callbacks are silently ignored
Issue tags: +Needs backport to D7

Would it be better to remove the function_exists() here?

geek-merlin’s picture

after some thinking about that, i think so:
a declared but missing callback can and should throw an error.
no need to waste microseconds with error detection here.

catch’s picture

Another option is call_user_func() or call_user_func_array() - those throw warnings if callbacks aren't available rather than fatal, however I'm not convinced this is something that should ever get out of sync so that you couldn't recover from it.

check_markup() is cached 99% of the time, and when it's not, what we do in this particular case is not going to have a measurable difference, the filters themselves are usually extremely costly to run.

geek-merlin’s picture

you are right.
to me the core point is: we should *never* have unfiltered content when it is supposed to be filtered, so our undefined function should trigger the "loudest" error possible.
("better a wsod that is fixed soon than a unrecognized security issue")

catch’s picture

Status: Active » Needs review
StatusFileSize
new868 bytes

Here it is with call_user_func(). This means that if D7 contrib modules or custom code are triggering this, and aren't updated between this getting committed and the next stable release, it'll be a warning instead of 500 when they upgrade.

I'd be OK with just using $function and no function_exists() check in D8, but I'd want to find the original issue that the function_exists() was added first.

geek-merlin’s picture

patch looks perfect.

but i have the feeling the other "function_exists" in this file should be addressed the same.

hunted down the commit: hook_filter signature change
http://drupalcode.org/project/drupal.git/commitdiff/2abbabbcc2f8662eb50a...

as of call_user_func i disagree: nonexisting callbacks should never be silently ignored, upgrade pain is better than security or debugging pain, but ymmv.

catch’s picture

Status: Needs review » Needs work

Call user func doesn't silently ignore, it throws a php warning. Looking at the original commit though, these just look like registry function_exists() that gave no purpose whatsoever, so let's remove them and use $function straight.

sun’s picture

Priority: Major » Normal

Agreed with #8. Just remove the additional condition, process callbacks are required, and documented as required.

However, I don't see how this is major. Critical/major for a contrib module perhaps, but not for core.

sun’s picture

Version: 8.x-dev » 7.x-dev
Status: Needs work » Patch (to be ported)

#1059884: Drop function_exists for 'callback' functions globally removed most/all function_exists() throughout core for D8.

New code to be ported:

  // Give filters the chance to escape HTML-like data such as code or formulas.
  foreach ($filters as $name => $filter) {
    if ($filter->status && isset($filter_info[$name]['prepare callback'])) {
      $function = $filter_info[$name]['prepare callback'];
      $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
    }
  }

  // Perform filtering.
  foreach ($filters as $name => $filter) {
    if ($filter->status && isset($filter_info[$name]['process callback'])) {
      $function = $filter_info[$name]['process callback'];
      $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
    }
  }
avr’s picture

Status: Patch (to be ported) » Needs review
Issue tags: -Needs backport to D7
StatusFileSize
new1.27 KB

Backported for D7.

sun’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Security, +API change

Thanks, @avr!

That's sufficient for me. I do not think this needs tests (and, how to test a fatal error in the first place? ;)).

This is a security improvement. But it is also a slight API change in that missing callbacks were silently ignored before, but that's clearly wrong behavior.

David_Rothstein’s picture

Status: Reviewed & tested by the community » Needs review

This is too risky to commit a couple days before 7.15 is released, so I'm leaving it for now... sorry.

I'm also pretty skeptical about committing this to Drupal 7 at all, though. Fatal errors are good if they occur when someone is developing new code (i.e., it definitely makes sense in Drupal 8). But not so good when they suddenly start occurring on live websites...

My vote would be to reconsider some of the earlier ideas in this issue (watchdog messages, PHP warnings that trigger via call_user_func(), etc). That way we still make noise about the error, but don't bring down a website for what in all likelihood is a not-very-serious problem.

Also note that the function_exists() may have been added in D7, but the actual behavior here (silently ignoring a missing process step) goes way back before that (at least to Drupal 5, and probably earlier). Because it used to be done by invoking the module's hook with a 'process' $op, which the module could easily have ignored.

geek-merlin’s picture

Status: Needs review » Reviewed & tested by the community

i strongly disagree.
if there are still modules out there (which i guess is not the case) whose serious bug (declaring a filter function that does not exist) is protected by this core bug, then having a wsod after upgrade (you test your site after upgrade, don't you?) is better than keeping a security hole (silently ignored filter).

in other terms: protecting security holes by buggy modules which rely on this bug is not an option.

xjm’s picture

Status: Reviewed & tested by the community » Needs review

The branch maintainer says a different approach would be preferred for D7, and I agree this would not be a good thing to backport. It'd be the missing bundle exception all over again. Let's try a different approach?

Edit: also, I checked with a member of the security team and this is not a security issue.

Status: Needs review » Closed (outdated)

Automatically closed because Drupal 7 security and bugfix support has ended as of 5 January 2025. If the issue verifiably applies to later versions, please reopen with details and update the version.