Creating a new node with this input filter enabled triggers a recursion error (resulting in segmentation fault or memory error under Apache).

To reproduce: Create a node with nid 1 and containing [[nid:1]], or title "Foo" and containing [[nodetitle:Foo]], etc. Alternatively, two nodes which reference each other similarly work, as should three nodes referencing each other in a circle, and so forth.

The following function calls recurse, and will occupy whatever resources they can get their hands on.

_freelinking_process
freelinking_get_freelink
_freelinking_build_freelink
call_user_func_array
freelinking_prepopulate_node_callback
freelinking_prepopulate_fields_from_page
node_load
node_load_multiple
entity_load
load
attachLoad
attachLoad
field_attach_load
_field_invoke_multiple
text_field_load
_text_sanitize
check_markup

The final check_markup() will then begin the process again, and webserver explodes, setting fire to the chicken coop.

Pretty sure this appears out of the box with the Drupal Wiki installation profile (maybe you have to enable to Wiki style text format first?).

Comments

xurizaemon’s picture

Issue summary: View changes

Updated issue summary.

xurizaemon’s picture

Status: Active » Needs work

#1516896: Self-reference causes Drupal Core common.inc fatal error looked like the same issue from the title, but not the details. Noted also at http://geoff.com.au/drupal-module/freelinking

xurizaemon’s picture

Setttings on site which it appears on, via drush @wiki vget freelink -

freelinking_cache: 0
freelinking_createnode_failover: "search"
freelinking_default: "createnode"
freelinking_drupalorgnid_http_request: 1
freelinking_file_file_path: "public://"
freelinking_match_syntax: "double_bracket"
freelinking_nodetitle_failover: "create"
freelinking_nodetitle_searchcontenttype: "wiki"
freelinking_path_basepath: ""
freelinking_path_failover: "none"
freelinking_prepopulate_node_advanced: Array
(
    [taxonomy] => 0
)

freelinking_prepopulate_node_type: "wiki"
freelinking_search_failover: "error"
freelinking_wikipedia_languagecode: "en"
wikitools_hijack_freelinking: 0

From #1516896: Self-reference causes Drupal Core common.inc fatal error (which mentioned the issue appearing if a page referenced itself), I wondered if this was triggered by having "recent content" block enabled, but disabling that block did not resolve the issue.

xurizaemon’s picture

Seems this requires the Freelinking plugin falling through to the create link, Freelinking Prepopulate enabled, and a node which links to a missing node. It may be necessary to disable or clear the cache to trigger it.

These both work, as may other combinations - basically, we need to end up at createnode + freelinking_prepopulate

freelinking_default: "nodetitle"
freelinking_nodetitle_failover: "create"
freelinking_default: "createnode"

#1624874: Port project issue text filter has a similar recursion issue for Project module.

dooug’s picture

Whew! Glad to see someone else has identified this headache as well. (Just curious, what did you use to trace the function calls?) I'm attempting to debug/patch this so we can use freelinking with the Wiki installation profile.

xurizaemon’s picture

Issue summary: View changes

Updated issue summary.

xurizaemon’s picture

Title: Recursion bug in D7 branch » Recursion bug in D7 branch when references are circular

I suspect this may require some refactoring.

node_load() seems to always trigger the text filters to run, which means that any circular freelinking references in displayed content will kill it off. I had been experimenting with using entity_load() instead to see if I could get it to not fire the node load hooks, but there was a separate issue I needed to deal with first so I stopped battling this ... for now.

@dooug, I think that I used debug_backtrace().

EDIT: Updated issue description thusly -

To reproduce: Create a node with nid 1 and containing [[nid:1]], or title "Foo" and containing [[Foo]], etc. Alternatively, two nodes which reference each other similarly work, as should three nodes referencing each other in a circle, and so forth.

bc’s picture

We were able to work around this issue by commenting out lines 69-73 in freelinking_prepopulate.module - freelinking_populate_fields_from_page has a lot of Drupal 6isms in it, and one of the arguments ends up being an empty array because of #1689918: Illegal choice message on Freelinking Settings form

Later on we'll work on removing any unusable features and we'll provide a patch on this ticket.

xurizaemon’s picture

Odds are the empty array is related to freelinking_prepopulate_list_fields() which (for me, anyway) gives an empty result on first call, and an array on second call. Something amiss in the static caching there?

bc’s picture

StatusFileSize
new869 bytes

patch attached - if $target['target'] and $target['dest'] are the same, link to current page and g.t.f.o. of the callback :)

this solves the link-to-current-page case.

bc’s picture

actually i'm totally wrong. please stand by :)

xurizaemon’s picture

Looks like you're aware current-page is only one cause of this bug, but just be clear - that approach probably won't help with circular (vs self) references, eg these two cases which I believe also trigger the same issue -

Title   Body
Node A  This is a node which relates to [[Node B]]
Node B  This is a node which relates to [[Node A]]

Title   Body
Node A  [[Node B]]
Node B  [[Node C]]
Node C  [[Node A]]
bc’s picture

another stab: this patch does a few things, but the big change is that i'm using the pathauto path to form the link. i like it this way more, but it's a pretty significant departure from how freelinking and drupal wikidom works. let's discuss this :) this patch isn't for mass consumption yet!

also, i've noticed that there are a handful of unimplemented / orphaned 6.x features in the 7.x branch, like og-related stuff and the no cache setting. if our budget allows, we'll work on pruning this stuff out.

xurizaemon’s picture

Thanks, but I think this isn't the right approach for a couple of reasons.

+++ b/plugins/freelinking_nodetitle.inc
@@ -18,24 +18,32 @@ $freelinking['nodetitle'] = array(
+  // get likely path title
+  $trimmed_path_title = trim($target['target']);
+  module_load_include('inc', 'pathauto', 'pathauto');
+  $link_path_title = pathauto_cleanstring($trimmed_path_title);
+
+  $source_path = drupal_lookup_path("source", variable_get('freelinking_path_basepath') . '/' . $link_path_title);
 

This looks wrong to me - both because it implicitly depends on pathauto, and because it replicates what url() should do. If you use url(), you should get the correct pathauto result anyway.

Maybe you found that this worked because url() triggered a node_load() and circumventing it didn't?

baldwinlouie’s picture

I ran into this problem also. I took a cue from the project_issue module and how it is preventing recursion. Here's the code snippet. I will generate a patch once I have time.

function freelinking_prepopulate_fields_from_page($fields, $plugin = 'nodecreate', $path = NULL) {
  static $prepopulate;
  static $entities = array();
  $query = array();
  $index = $plugin . serialize($fields);
  if (!$prepopulate[$index]) {
    if (is_array(freelinking_prepopulate_list_fields($plugin))) {
      $prepopulate[$index] = array_intersect_key(freelinking_prepopulate_list_fields($plugin), $fields);
    }
  }
  // recursion protection
  if ($plugin == 'nodecreate' && arg(0) == 'node' && is_numeric(arg(1))) {
    $nid = arg(1);
    $running = isset($entities[$nid]) ? $entities[$nid] : FALSE;
    if ($running == TRUE) {
      return;
    }
    else {
      $entities[$nid] = TRUE;
      $object = node_load($nid);
      $entities[$nid] = FALSE;
    }
  }
  if (isset($prepopulate[$index]) && is_array($prepopulate[$index])) {
    foreach ($prepopulate[$index] as $field => $definition) {
      switch ($field) {
        case 'og':
          if (isset($object->og_group_ref)) {
            $query[$definition['prepopulate']] = $object->og_group_ref[LANGUAGE_NONE][0]['target_id'];
          }
          break;
        case 'taxonomy':
          if (!isset($object) || !$object->taxonomy) {
            break;
          }
          foreach ($object->taxonomy as $term) {
            $query[$definition['prepopulate'] . '[' . $term->vid . ']'] .= $term->name . ',';
          }
          break;
        case 'book':
          if ($node->book) {
            $query['parent'] = $object->book['mlid'];
          }
          break;
        default:
          if ($object->$field) {
            $query[$definition['prepopulate']] = $object->$field;
          }
          break;
      }
    }
  }
  return $query;
}

xurizaemon’s picture

@baldwinlouie - this looks pretty good! Nice.

I managed to get one error out of it while saving a node with an intentionally broken nodetitle reference - Notice: Trying to get property of non-object in l() (line 2357 of drupal-7.16/includes/common.inc).

Unsure if that's a Freelinking issue or related to your patch, though. While the site I'm testing on is bare D7.16 with only FL and submodules enabled, I did create it a few months back so I'm not 100% of its purity. The issue I'm seeing is that $options['language'] = 'url' when passed to l() - l() expects a language object there instead of a language code.

Here's your fix above as a patch, anyway.

xurizaemon’s picture

Status: Needs work » Needs review

Setting "Needs review". Reviewer, please verify that you do not see a 'property of non-object' message if creating a broken nodetitle reference (eg [[nodetitle:ThisNodeDoesNotExist]] / [[ThisNodeDoesNotExist]]).

xurizaemon’s picture

Suspect the test for $plugin == 'nodecreate' means that this recursion protection will not apply if the module has been configured to use different default / fallback plugins. Need to test this also.

pmackay’s picture

I've installed v3.2 and am finding the out of memory problem if set the "If a suitable content is not found " setting to "Add a link to create content (Without permission: Access Denied)" and then create a link on a page that doesnt have a destination page. This seems slightly different to the scenario described in this bug report, hence wondering if it needs a separate issue?

xurizaemon’s picture

@pmackay - grab 7.x-3.x and apply the patch above to it, then see if the issue persists?

If so, it would be helpful for you to debug - identify the block of code where you're hitting the memory limit, and add some debugging code to see how it's happening (and hopefully how to prevent it).

ezheidtmann’s picture

StatusFileSize
new2.16 KB

I tested the patch in #14 and made one small change: return $query when recursion is detected. This avoids an error from array_merge(). grobot, I did not see a non-object error even though my error reporting settings would show it.

I tested this in the situation where the variable "freelinking_prepopulate_node_advanced" is an empty array. I haven't tested with other fields enabled.

ezheidtmann’s picture

Re #16: the node_load() call is not executed for other plugins, so recursion protection is not needed.

ezheidtmann’s picture

StatusFileSize
new2.11 KB

Here's a slightly improved version of #19. Fewer lines, more readable IMO.

jarodms’s picture

Patch #21 gave me the following error:
Notice: Undefined property: stdClass::$taxonomy in freelinking_prepopulate_fields_from_page() (line 58 of ..\freelinking\modules\freelinking_prepopulate\freelinking_prepopulate.utilities.inc).

Including my minor patch of that

jarodms’s picture

Issue summary: View changes

Updated issue summary.

derekwebb1’s picture

Issue summary: View changes

#22 Seems to solve the issue for me so far. Thanks!

xurizaemon’s picture

@deeporange1, you said #22 fixed it for you, but your changes hid the patch from #22 and left patch from #8 showing - was that intentional?

I've un-hid #22 and left #8 showing for now - please hide #8 if that was what you meant to do.

derekwebb1’s picture

Sorry, I am not 100% sure if I should have set the various files to hidden. Now I cannot seem to do so.

However, in my codebase I only applied the patch from #22 and that did fix the issue for me so far. I did not apply any other patches.

gisle’s picture

Status: Needs review » Closed (cannot reproduce)

It looks like this is fixed in 7.x-3.3.