diff --git a/modules/node/node.module b/modules/node/node.module
index f892d1c..4294aa7 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -3338,7 +3338,7 @@ function _node_query_node_access_alter($query, $type) {
     return;
   }
 
-  $tables = $query->getTables();
+  $tables = &$query->getTables();
   $base_table = $query->getMetaData('base_table');
   // If no base table is specified explicitly, search for one.
   if (!$base_table) {
@@ -3422,20 +3422,22 @@ function _node_query_node_access_alter($query, $type) {
       $grant_conditions = db_or();
       // If any grant exists for the specified user, then user has access
       // to the node for the specified operation.
+      $counter = 0;
       foreach ($grants as $realm => $gids) {
         foreach ($gids as $gid) {
           $grant_conditions->condition(db_and()
-            ->condition('na.gid', $gid)
-            ->condition('na.realm', $realm)
+            ->where('na.gid = :gid_' . $counter . '_' . $gid, array(':gid_' . $counter . '_' . $gid => $gid))
+            ->where('na.realm = :realm_' . $counter . '_' . $gid, array(':realm_' . $counter . '_' . $gid => $realm))
           );
         }
+        $counter++;
       }
 
       // Attach conditions to the subquery for nodes.
       if (count($grant_conditions->conditions())) {
         $subquery->condition($grant_conditions);
       }
-      $subquery->condition('na.grant_' . $op, 1, '>=');
+      $subquery->where('na.grant_' . $op . ' >= 1');
       $field = 'nid';
       // Now handle entities.
       if ($type == 'entity') {
@@ -3451,7 +3453,17 @@ function _node_query_node_access_alter($query, $type) {
       }
       // Otherwise attach it to the node query itself.
       else {
-        $query->exists($subquery);
+        if (empty($tableinfo['join type'])) {
+          // If we are looking at the main table of the query, apply the
+          // subquery directly.
+          $query->exists($subquery);
+        }
+        else {
+          // If we are looking at a joined table, add the node access check
+          // to the join condition.
+          $tables[$nalias]['condition'] .= ' AND EXISTS(' . (string)$subquery . ')';
+          $tables[$nalias]['arguments'] += $subquery->arguments();
+        }
       }
     }
   }
diff --git a/modules/node/node.test b/modules/node/node.test
index 5c9118e..5a9e9bb 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -2842,6 +2842,132 @@ class NodeEntityViewModeAlterTest extends NodeWebTestCase {
   }
 }
 
+ /**
+ * Tests the interaction of the node access system with joined Query objects.
+ */
+class NodeAccessSubqueryTest extends NodeWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Node access subqueries',
+      'description' => 'Tests that node access checks get applied to the node base table across referenced fields.',
+      'group' => 'Node',
+    );
+  }
+
+  public function setUp() {
+    $modules = array('node_access_test');
+    parent::setUp($modules);
+
+    node_access_rebuild();
+    variable_set('node_access_test_private', TRUE);
+
+    // Create some users.
+    $this->admin_user = $this->drupalCreateUser(array('access content', 'bypass node access'));
+    $this->user = $this->drupalCreateUser(array('access content'));
+
+    // Add a custom field to the page content type.
+    $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $this->field = field_create_field(
+      array(
+        'field_name' => $this->field_name,
+        'type' => 'number_integer',
+        'cardinality' => FIELD_CARDINALITY_UNLIMITED
+      )
+    );
+    $instance = array(
+      'field_name' => $this->field_name,
+      'entity_type' => 'node',
+      'bundle' => 'page',
+    );
+    $this->instance = field_create_instance($instance);
+  }
+
+  /**
+   * Tests grants in subqueries.
+   */
+  function testNodeAccessSubquery() {
+    // Create a page node.
+    $langcode = LANGUAGE_NONE;
+    $field_data = array();
+    $nodes = array();
+
+    $nodes[1] = $this->drupalCreateNode(array('title' => 'Private node 1', 'private'=> TRUE, 'uid' => $this->admin_user->uid, 'status'=>0));
+    $nodes[2] = $this->drupalCreateNode(array(
+      'title' => 'Public node 2',
+      $this->field_name => array(
+        $langcode => array(
+          0 => array(
+            'value' => $nodes[1]->nid
+          )
+        )
+      ),
+      'private' => FALSE,
+      'uid' => $this->user->uid,
+      'status'=>1,
+    ));
+    $nodes[3] = $this->drupalCreateNode(array('title' => 'Public node 3', 'private' => TRUE, 'uid' => $this->user->uid, 'status'=>1));
+    $nodes[4] = $this->drupalCreateNode(array(
+      'title' => 'Public node 4',
+      $this->field_name => array(
+        $langcode => array(
+          0 => array(
+            'value' => $nodes[3]->nid
+          )
+        )
+      ),
+      'private' => FALSE,
+      'uid' => $this->user->uid,
+      'status'=>1,
+    ));
+    $nodes[5] = $this->drupalCreateNode(array('title' => 'Public node 5', 'uid' => $this->user->uid, 'private' => FALSE, 'status'=>1));
+
+    $expected_admin_count = 5; // one row for each node
+    $expected_user_count = 4; // one row for each node with my uid
+
+    // Set up template query.
+    $join_table = _field_sql_storage_tablename($this->field);
+    $join_column = $this->field_name . '_value';
+
+    $base_query = db_select('node', 'n');
+    $base_query->addTag('node_access');
+    $base_query->addJoin('LEFT OUTER', $join_table, 'jf', 'n.vid = jf.revision_id');
+
+    // Now add subquery join.
+    $base_query->addJoin('LEFT OUTER', 'node', 's', 'jf.' . $join_column .' = s.nid');
+    $base_query
+      ->fields('n', array('nid', 'title'))
+      ->fields('s', array('nid', 'title'));
+
+    // We need to clone, because the node_access tag is only altered once.
+    $query = clone $base_query;
+    $query->addMetaData('account', $this->admin_user);
+    $num_rows = $query->countQuery()->execute()->fetchField();
+    $this->assertEqual($num_rows, $expected_admin_count, "Admin user should get $expected_admin_count rows returned after initial load. Actual: $num_rows");
+
+    // Need a fresh copy of the query, because the node access alteration happens in the pre-execute phase, which only happens once.
+    $query = clone $base_query;
+    $query->addMetaData('account', $this->user);
+    $num_rows = $query->countQuery()->execute()->fetchField();
+    $this->assertEqual($num_rows, $expected_user_count, "Regular user should get $expected_user_count rows returned after initial load. Actual: $num_rows");
+
+    // Examine row for Public Node 2 -- test whether reference to private node is excluded as appropriate
+    $base_query->condition('n.nid', $nodes[2]->nid);
+
+    $query = clone $base_query;
+    $query->addMetaData('account', $this->admin_user);
+    $row = $query->execute()->fetchAssoc();
+    $this->assertNotNull($row, "Admin user should see node containing reference");
+    $this->assertNotNull($row['s_nid'], "Admin user should see reference to private node");
+
+    $query = clone $base_query;
+    $query->addMetaData('account', $this->user);
+    $row = $query->execute()->fetchAssoc();
+    $this->assertNotNull($row, "Regular user should see node containing reference");
+    $this->assertNull($row['s_nid'], "Regular user should not see reference to private node");
+  }
+}
+
 /**
  * Tests the cache invalidation of node operations.
  */
