Copilot commented on code in PR #3592:
URL: https://github.com/apache/brpc/pull/3592#discussion_r4219267478


##########
src/brpc/policy/naming_service_json.h:
##########
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+#ifndef BRPC_POLICY_NAMING_SERVICE_JSON_H
+#define BRPC_POLICY_NAMING_SERVICE_JSON_H
+
+#include <string>
+#include "butil/third_party/rapidjson/document.h"
+
+namespace brpc {
+namespace policy {
+
+// Maximum nesting depth of JSON replies accepted from naming-service
+// backends. Legit replies of consul/nacos/discovery are only a few levels
+// deep, deeper replies are treated as malformed and rejected before
+// parsing: rapidjson builds, visits and destroys its DOM recursively, so
+// unbounded nesting could exhaust the stack even though the parsing itself
+// is iterative (same rationale as json2pb's kParseIterativeFlag plus
+// json2pb_max_recursion_depth).
+const int kMaxNamingServiceJsonDepth = 100;
+
+// Count the maximum nesting depth of '{'/'[' in `text', ignoring brackets
+// inside string literals.
+inline int MaxJsonDepth(const std::string& text) {
+    int depth = 0;
+    int max_depth = 0;
+    bool in_string = false;
+    bool escaped = false;
+    for (size_t i = 0; i < text.size(); ++i) {
+        const char c = text[i];
+        if (in_string) {
+            if (escaped) {
+                escaped = false;
+            } else if (c == '\\') {
+                escaped = true;
+            } else if (c == '"') {
+                in_string = false;
+            }
+        } else if (c == '"') {
+            in_string = true;
+        } else if (c == '{' || c == '[') {
+            if (++depth > max_depth) {
+                max_depth = depth;
+                if (max_depth > kMaxNamingServiceJsonDepth) {
+                    // Too deep, no need to scan further.
+                    return max_depth;
+                }
+            }
+        } else if (c == '}' || c == ']') {
+            --depth;
+        }
+    }
+    return max_depth;
+}
+
+// Parse a JSON reply of a naming service into `doc' iteratively. Returns
+// false if `text' is not valid JSON or is nested deeper than
+// kMaxNamingServiceJsonDepth, `doc' is left empty in the latter case.
+inline bool ParseNamingServiceJson(const std::string& text,
+                                   BUTIL_RAPIDJSON_NAMESPACE::Document* doc) {
+    if (MaxJsonDepth(text) > kMaxNamingServiceJsonDepth) {

Review Comment:
   The comment says "`doc` is left empty" when the depth limit is exceeded, but 
the function returns `false` without clearing `*doc`, so callers may observe 
stale contents from a previous successful parse. Either clear `doc` before 
returning (e.g., `doc->SetNull()`), or adjust the comment to reflect current 
behavior.



##########
src/brpc/policy/naming_service_json.h:
##########
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+#ifndef BRPC_POLICY_NAMING_SERVICE_JSON_H
+#define BRPC_POLICY_NAMING_SERVICE_JSON_H
+
+#include <string>
+#include "butil/third_party/rapidjson/document.h"
+
+namespace brpc {
+namespace policy {
+
+// Maximum nesting depth of JSON replies accepted from naming-service
+// backends. Legit replies of consul/nacos/discovery are only a few levels
+// deep, deeper replies are treated as malformed and rejected before
+// parsing: rapidjson builds, visits and destroys its DOM recursively, so
+// unbounded nesting could exhaust the stack even though the parsing itself
+// is iterative (same rationale as json2pb's kParseIterativeFlag plus
+// json2pb_max_recursion_depth).
+const int kMaxNamingServiceJsonDepth = 100;
+
+// Count the maximum nesting depth of '{'/'[' in `text', ignoring brackets
+// inside string literals.
+inline int MaxJsonDepth(const std::string& text) {
+    int depth = 0;
+    int max_depth = 0;
+    bool in_string = false;
+    bool escaped = false;
+    for (size_t i = 0; i < text.size(); ++i) {
+        const char c = text[i];
+        if (in_string) {
+            if (escaped) {
+                escaped = false;
+            } else if (c == '\\') {
+                escaped = true;
+            } else if (c == '"') {
+                in_string = false;
+            }
+        } else if (c == '"') {
+            in_string = true;
+        } else if (c == '{' || c == '[') {
+            if (++depth > max_depth) {
+                max_depth = depth;
+                if (max_depth > kMaxNamingServiceJsonDepth) {
+                    // Too deep, no need to scan further.
+                    return max_depth;
+                }
+            }
+        } else if (c == '}' || c == ']') {
+            --depth;
+        }
+    }
+    return max_depth;
+}
+
+// Parse a JSON reply of a naming service into `doc' iteratively. Returns
+// false if `text' is not valid JSON or is nested deeper than
+// kMaxNamingServiceJsonDepth, `doc' is left empty in the latter case.
+inline bool ParseNamingServiceJson(const std::string& text,
+                                   BUTIL_RAPIDJSON_NAMESPACE::Document* doc) {
+    if (MaxJsonDepth(text) > kMaxNamingServiceJsonDepth) {
+        return false;
+    }
+    doc->Parse<BUTIL_RAPIDJSON_NAMESPACE::kParseIterativeFlag>(text.c_str());
+    return !doc->HasParseError();
+}

Review Comment:
   `Document::Parse(text.c_str())` parses as a null-terminated C-string; if 
`text` ever contains embedded `\0`, the parser will stop early while 
`MaxJsonDepth(text)` scans the full buffer, creating an inconsistency (and 
potentially allowing tail content to bypass the depth guard). Prefer parsing 
with an explicit length (e.g., `text.data()` + `text.size()`) so the same bytes 
are scanned and parsed.



##########
src/brpc/policy/naming_service_json.h:
##########
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+#ifndef BRPC_POLICY_NAMING_SERVICE_JSON_H
+#define BRPC_POLICY_NAMING_SERVICE_JSON_H
+
+#include <string>
+#include "butil/third_party/rapidjson/document.h"
+
+namespace brpc {
+namespace policy {
+
+// Maximum nesting depth of JSON replies accepted from naming-service
+// backends. Legit replies of consul/nacos/discovery are only a few levels
+// deep, deeper replies are treated as malformed and rejected before
+// parsing: rapidjson builds, visits and destroys its DOM recursively, so
+// unbounded nesting could exhaust the stack even though the parsing itself
+// is iterative (same rationale as json2pb's kParseIterativeFlag plus
+// json2pb_max_recursion_depth).
+const int kMaxNamingServiceJsonDepth = 100;
+
+// Count the maximum nesting depth of '{'/'[' in `text', ignoring brackets
+// inside string literals.
+inline int MaxJsonDepth(const std::string& text) {
+    int depth = 0;
+    int max_depth = 0;
+    bool in_string = false;
+    bool escaped = false;
+    for (size_t i = 0; i < text.size(); ++i) {
+        const char c = text[i];
+        if (in_string) {
+            if (escaped) {
+                escaped = false;
+            } else if (c == '\\') {
+                escaped = true;
+            } else if (c == '"') {
+                in_string = false;
+            }
+        } else if (c == '"') {
+            in_string = true;
+        } else if (c == '{' || c == '[') {
+            if (++depth > max_depth) {
+                max_depth = depth;
+                if (max_depth > kMaxNamingServiceJsonDepth) {
+                    // Too deep, no need to scan further.
+                    return max_depth;
+                }
+            }
+        } else if (c == '}' || c == ']') {
+            --depth;
+        }
+    }
+    return max_depth;
+}

Review Comment:
   `depth` is decremented unconditionally on `}`/`]`. For malformed inputs with 
many closing brackets early, `depth` can go negative; on sufficiently large 
inputs this can underflow `int`, which is avoidable even if the subsequent JSON 
parse would fail. Clamping `depth` at 0 (or only decrementing when `depth > 0`) 
makes the pre-scan more robust without affecting valid JSON behavior.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to