Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions any.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "google/protobuf/any.h"

#include "google/protobuf/arenastring.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/generated_message_util.h"
#include "google/protobuf/message.h"

// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace internal {

bool AnyMetadata::PackFrom(Arena* arena, const Message& message) {
return PackFrom(arena, message, kTypeGoogleApisComPrefix);
}

bool AnyMetadata::PackFrom(Arena* arena, const Message& message,
absl::string_view type_url_prefix) {
type_url_->Set(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

状态不一致:line 51 处先更新了 type_url_,但 line 53 处 SerializeToString 可能返回 false。当序列化失败时,Any 对象会处于 type_url 已变更但 value 未同步的不一致状态,后续调用 UnpackTo 可能引发解析错误或数据损坏。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 51

💬 详细说明:

  • 状态不一致:line 51 处先更新了 type_url_,但 line 53 处 SerializeToString 可能返回 false。当序列化失败时,Any 对象会处于 type_url 已变更但 value 未同步的不一致状态,后续调用 UnpackTo 可能引发解析错误或数据损坏。

📝 问题代码:

  type_url_->Set(

💡 修复建议:

调整执行顺序,先获取 value 指针并尝试序列化,确认成功后再更新 type_url_,确保操作的原子性且避免额外内存分配。

✅ 修复示例:

  std::string* value = value_->Mutable(arena);
  if (!message.SerializeToString(value)) {
    return false;
  }
  type_url_->Set(
      GetTypeUrl(message.GetDescriptor()->full_name(), type_url_prefix), arena);
  return true;

🔗 参考链接

GetTypeUrl(message.GetDescriptor()->full_name(), type_url_prefix), arena);
return message.SerializeToString(value_->Mutable(arena));
}

bool AnyMetadata::UnpackTo(Message* message) const {
if (!InternalIs(message->GetDescriptor()->full_name())) {
return false;
}
return message->ParseFromString(value_->Get());
}

bool GetAnyFieldDescriptors(const Message& message,
const FieldDescriptor** type_url_field,
const FieldDescriptor** value_field) {
const Descriptor* descriptor = message.GetDescriptor();
if (descriptor->full_name() != kAnyFullTypeName) {
return false;
}
*type_url_field = descriptor->FindFieldByNumber(1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

输出参数未初始化导致的不确定状态
在 GetAnyFieldDescriptors 函数中,line 70 和 line 71 直接对输出参数 *type_url_field*value_field 进行赋值。如果调用者传入的指针本身是未初始化的(例如局部变量 const FieldDescriptor* f;),且函数在 line 68 处因类型不匹配提前返回 false,则这两个输出参数将保持未初始化状态。调用者若仅检查返回值而忽略了对输出参数的重置或初始化,后续使用这些指针将导致未定义行为(UB)。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 70

💬 详细说明:

  • 风险影响:当传入非 Any 类型的 Message 时,函数返回 false 但输出参数未被置为 nullptr。若调用者逻辑依赖于“失败时输出参数为空”的隐式契约,可能会误用野指针。

📝 问题代码:

  *type_url_field = descriptor->FindFieldByNumber(1);

💡 修复建议:

建议在函数入口处将输出参数显式初始化为 nullptr,或在 line 68 返回前将其置空。这样可以确保无论成功还是失败,输出参数都处于确定的安全状态。

✅ 修复示例:

bool GetAnyFieldDescriptors(const Message& message,
                            const FieldDescriptor** type_url_field,
                            const FieldDescriptor** value_field) {
  *type_url_field = nullptr;
  *value_field = nullptr;
  const Descriptor* descriptor = message.GetDescriptor();
  if (descriptor->full_name() != kAnyFullTypeName) {
    return false;
  }
  *type_url_field = descriptor->FindFieldByNumber(1);
  *value_field = descriptor->FindFieldByNumber(2);
  return (*type_url_field != nullptr &&
          (*type_url_field)->type() == FieldDescriptor::TYPE_STRING &&
          *value_field != nullptr &&
          (*value_field)->type() == FieldDescriptor::TYPE_BYTES);
}

🔗 参考链接

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

输出参数在错误路径下可能未被赋值
line 70-71 处,函数先对 *type_url_field*value_field 进行赋值。如果 line 72-75 的校验逻辑失败(例如字段类型不匹配),函数返回 false。此时,调用者传入的 *type_url_field*value_field 已被修改为 FindFieldByNumber 的返回值(可能为 nullptr),但调用者可能误以为返回 false 时输出参数保持原值或未被触碰。更严重的是,如果 FindFieldByNumber 返回非空指针但类型校验失败,输出参数将指向一个“存在但类型错误”的字段描述符,调用者若忽略返回值直接使用,可能导致后续逻辑错误。
虽然 line 72-75 进行了校验,但如果校验失败,输出参数的状态是“已赋值但无效”。建议在校验失败前不要修改输出参数,或者确保调用者严格检查返回值。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 70

💬 详细说明:

  • 调用者若未严格检查返回值而直接使用输出参数,可能访问到类型不匹配的字段描述符,导致运行时错误或逻辑异常。

📝 问题代码:

*type_url_field = descriptor->FindFieldByNumber(1);

💡 修复建议:

建议采用临时变量存储查找结果,待所有校验通过后再赋值给输出参数,确保原子性。

✅ 修复示例:

bool GetAnyFieldDescriptors(const Message& message,
                            const FieldDescriptor** type_url_field,
                            const FieldDescriptor** value_field) {
  const Descriptor* descriptor = message.GetDescriptor();
  if (descriptor->full_name() != kAnyFullTypeName) {
    return false;
  }
  const FieldDescriptor* type_url = descriptor->FindFieldByNumber(1);
  const FieldDescriptor* value = descriptor->FindFieldByNumber(2);
  if (type_url == nullptr || type_url->type() != FieldDescriptor::TYPE_STRING ||
      value == nullptr || value->type() != FieldDescriptor::TYPE_BYTES) {
    return false;
  }
  *type_url_field = type_url;
  *value_field = value;
  return true;
}

🔗 参考链接

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

输出参数在错误路径下未被初始化
line 70-71 处,当 FindFieldByNumber 返回 nullptr 时(例如字段不存在),代码直接跳转到 line 72 的 return 语句。此时 *type_url_field*value_field 已被赋值为 nullptr,但调用者可能期望在返回 false 时这些指针保持未修改或为 nullptr。更严重的是,如果调用者在返回 false 后仍然尝试解引用这些指针(假设它们被更新了),会导致空指针解引用。虽然 line 72-75 检查了返回值,但调用者可能忽略返回值直接使用指针。
触发条件:当 message 的 Descriptor 中缺少字段 1 或字段 2,或者字段类型不匹配时,函数返回 false,但输出参数已被设置为 nullptr。如果调用者不检查返回值就使用这些指针,会崩溃。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 70

💬 详细说明:

  • 如果调用者忽略返回值并解引用输出参数,会导致程序崩溃。影响所有调用 GetAnyFieldDescriptors 的代码路径。

📝 问题代码:

  *type_url_field = descriptor->FindFieldByNumber(1);

💡 修复建议:

在赋值给输出参数之前,先验证 FindFieldByNumber 的返回值不为 nullptr。或者在函数开始时将输出参数初始化为 nullptr,确保在错误路径下它们处于安全状态。建议在 line 69 之后、line 70 之前添加 nullptr 初始化,并在 line 72 的检查中确保只有当两个字段都有效时才返回 true。

✅ 修复示例:

bool GetAnyFieldDescriptors(const Message& message,
                            const FieldDescriptor** type_url_field,
                            const FieldDescriptor** value_field) {
  const Descriptor* descriptor = message.GetDescriptor();
  if (descriptor->full_name() != kAnyFullTypeName) {
    *type_url_field = nullptr;
    *value_field = nullptr;
    return false;
  }
  *type_url_field = descriptor->FindFieldByNumber(1);
  *value_field = descriptor->FindFieldByNumber(2);
  if (*type_url_field == nullptr || *value_field == nullptr) {
    *type_url_field = nullptr;
    *value_field = nullptr;
    return false;
  }
  return ((*type_url_field)->type() == FieldDescriptor::TYPE_STRING &&
          (*value_field)->type() == FieldDescriptor::TYPE_BYTES);
}

🔗 参考链接

*value_field = descriptor->FindFieldByNumber(2);
return (*type_url_field != nullptr &&
(*type_url_field)->type() == FieldDescriptor::TYPE_STRING &&
*value_field != nullptr &&
(*value_field)->type() == FieldDescriptor::TYPE_BYTES);
}

} // namespace internal
} // namespace protobuf
} // namespace google

#include "google/protobuf/port_undef.inc"