Index: wp-includes/rewrite.php
===================================================================
--- wp-includes/rewrite.php	(revision 13450)
+++ wp-includes/rewrite.php	(working copy)
@@ -1281,6 +1281,8 @@
 	 * @return array Rewrite rule list.
 	 */
 	function generate_rewrite_rules($permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true) {
+		$permalink_structure = preg_quote( $permalink_structure, '#' );
+
 		//build a regex to match the feed section of URLs, something like (feed|atom|rss|rss2)/?
 		$feedregex2 = '';
 		foreach ( (array) $this->feeds as $feed_name) {
Index: wp-includes/classes.php
===================================================================
--- wp-includes/classes.php	(revision 13450)
+++ wp-includes/classes.php	(working copy)
@@ -193,50 +193,81 @@
 				$request = $req_uri;
 			}
 
+			if ( false !== strpos( $wp_rewrite->permalink_structure, '?' ) && !empty( $_SERVER['QUERY_STRING'] ) ) {
+				list( $qs ) = explode( '&', $_SERVER['QUERY_STRING'], 2 ); // only want the first piece
+				if ( false === strpos( $qs, '=' ) ) { // otherwise it's some non-permalink query arg
+					$request_no_qs = $request;
+					$request .= '?' . $qs;
+				}
+			} else {
+				$request_no_qs = false;
+			}
+
 			$this->request = $request;
+			$fallback = false;
+			$fallback_query = false;
 
 			// Look for matches.
 			$request_match = $request;
+			$request_match_no_qs = $request_no_qs;
+
 			foreach ( (array) $rewrite as $match => $query) {
+				$match = preg_replace( '#\[\^([^\]])+\]#', '[^\\1?]', $match );
+				$match = str_replace( array( '.*', '.+' ), array( '[^?]*', '[^?]+' ), $match );
 				// Don't try to match against AtomPub calls
 				if ( $req_uri == 'wp-app.php' )
 					break;
 
 				// If the requesting file is the anchor of the match, prepend it
 				// to the path info.
-				if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) )
+				if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) ) {
 					$request_match = $req_uri . '/' . $request;
+					if ( $request_match_no_qs )
+						$request_match_no_qs = $req_uri . '/' . $request_no_qs;
+				}
 
 				if ( preg_match("#^$match#", $request_match, $matches) ||
 					preg_match("#^$match#", urldecode($request_match), $matches) ) {
 					// Got a match.
 					$this->matched_rule = $match;
+					break;
+				} elseif ( $request_match_no_qs && !$fallback && ( preg_match("#^$match#", $request_match, $matches) ||
+					preg_match("#^$match#", urldecode($request_match), $matches) ) ) {
+					// Got a match, but there might be a better one in the other branch
+					$fallback = $match;
+					$fallback_query = $query;
+					continue;
+				}
+			}
 
-					// Trim the query of everything up to the '?'.
-					$query = preg_replace("!^.+\?!", '', $query);
+			if ( !$this->matched_rule && $fallback ) { // 2nd branch had the only match
+				$this->matched_rule = $fallback;
+				$query = $fallback_query;
+			}
 
-					// Substitute the substring matches into the query.
-					$query = addslashes(WP_MatchesMapRegex::apply($query, $matches));
+			if ( $this->matched_rule ) {
+				// Trim the query of everything up to the '?'.
+				$query = preg_replace("!^.+\?!", '', $query);
 
-					$this->matched_query = $query;
+				// Substitute the substring matches into the query.
+				$query = addslashes(WP_MatchesMapRegex::apply($query, $matches));
 
-					// Parse the query.
-					parse_str($query, $perma_query_vars);
+				$this->matched_query = $query;
 
-					// If we're processing a 404 request, clear the error var
-					// since we found something.
-					if ( isset($_GET['error']) )
-						unset($_GET['error']);
+				// Parse the query.
+				parse_str($query, $perma_query_vars);
 
-					if ( isset($error) )
-						unset($error);
+				// If we're processing a 404 request, clear the error var
+				// since we found something.
+				if ( isset($_GET['error']) )
+					unset($_GET['error']);
 
-					break;
-				}
+				if ( isset($error) )
+					unset($error);
 			}
 
 			// If req_uri is empty or if it is a request for ourself, unset error.
-			if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {
+			if ( empty($request_no_qs) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {
 				if ( isset($_GET['error']) )
 					unset($_GET['error']);
 
