Skip to content
Open
2 changes: 1 addition & 1 deletion app/rest/request_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class RequestJson : public Request {
// Generate JSON string.
std::string json;
bool generate_status =
GenerateText(*parser_, builder.GetBufferPointer(), &json);
GenerateText(*parser_, builder.GetBufferPointer(), &json) == nullptr;
Comment on lines 68 to +69

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

Comparing the result of GenerateText to nullptr is incorrect. GenerateText returns a bool (indicating success or failure), not a pointer.

Comparing a bool to nullptr will either fail to compile in modern C++ or result in inverted logic (where generate_status becomes false on success and true on failure), causing the subsequent FIREBASE_ASSERT_RETURN_VOID(generate_status) to trigger on successful execution.

We should revert this to simply assign the bool result of GenerateText directly.

Suggested change
bool generate_status =
GenerateText(*parser_, builder.GetBufferPointer(), &json);
GenerateText(*parser_, builder.GetBufferPointer(), &json) == nullptr;
bool generate_status =
GenerateText(*parser_, builder.GetBufferPointer(), &json);

FIREBASE_ASSERT_RETURN_VOID(generate_status);

set_post_fields(json.c_str());
Expand Down
4 changes: 4 additions & 0 deletions app/src/variant_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ Variant FlexbufferToVariant(const flexbuffers::Reference& ref) {
case flexbuffers::FBT_BLOB:
LogError("Flexbuffers containing blobs are not supported.");
break;
case flexbuffers::FBT_MAX_TYPE:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;
Comment on lines +253 to +256

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using case flexbuffers::FBT_MAX_TYPE: only handles the specific sentinel value FBT_MAX_TYPE. If the flexbuffer is corrupted or contains an invalid/unknown type ID not covered by the switch, it will bypass this check entirely and silently return Variant::Null().

Using a default: case instead is much more robust as it will catch FBT_MAX_TYPE as well as any other unknown or corrupted type values.

Suggested change
case flexbuffers::FBT_MAX_TYPE:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;
default:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;

}
return Variant::Null();
}
Expand Down
3 changes: 1 addition & 2 deletions build_scripts/android/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,5 @@ if [[ $(uname) == "Linux" ]] || [[ $(uname) == "Darwin" ]]; then
else
# rsync has to be specifically installed on windows bash (including github runners)
# Also, rsync with absolute destination path doesn't work on Windows.
# Using a simple copy instead of rsync on Windows.
cp -R --parents "${paths[@]}" "${absbuildpath}"
cp -RL --parents "${paths[@]}" "${absbuildpath}"
fi
7 changes: 4 additions & 3 deletions cmake/external/flatbuffers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ if(TARGET flatbuffers OR NOT DOWNLOAD_FLATBUFFERS)
return()
endif()

set(version 99aa1ef21dd9dc3f9d4fb0eb82f4b59d0bb5e4c5)
# Commit corresponds to Tag v25.12.19-2026-02-06-03fffb2
set(version 95fda8c23e6e30ebd975892b5fad01efa53e039c)
set(patch_file
${CMAKE_CURRENT_LIST_DIR}/../../scripts/git/patches/flatbuffers/0001-remove-unused-var.patch)
${CMAKE_CURRENT_LIST_DIR}/../../scripts/git/patches/flatbuffers/0001-fix-error-macro.patch)

ExternalProject_Add(
flatbuffers
Expand All @@ -29,7 +30,7 @@ ExternalProject_Add(
COMMAND git init flatbuffers
COMMAND cd flatbuffers && git fetch --depth=1 https://github.com/google/flatbuffers.git ${version} && git reset --hard FETCH_HEAD

PATCH_COMMAND git apply ${patch_file} && git gc --aggressive
PATCH_COMMAND git apply ${patch_file}
PREFIX ${PROJECT_BINARY_DIR}

CONFIGURE_COMMAND ""
Expand Down
4 changes: 4 additions & 0 deletions database/src/desktop/persistence/flatbuffer_conversions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ Variant FlexbufferToVariant(const flexbuffers::Reference& ref) {
case flexbuffers::FBT_BLOB:
LogError("Flexbuffers containing blobs are not supported.");
break;
case flexbuffers::FBT_MAX_TYPE:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;
Comment on lines +106 to +109

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using case flexbuffers::FBT_MAX_TYPE: only handles the specific sentinel value FBT_MAX_TYPE. If the flexbuffer is corrupted or contains an invalid/unknown type ID not covered by the switch, it will bypass this check entirely and silently return Variant::Null().

Using a default: case instead is much more robust as it will catch FBT_MAX_TYPE as well as any other unknown or corrupted type values.

Suggested change
case flexbuffers::FBT_MAX_TYPE:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;
default:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;

}
return Variant::Null();
}
Expand Down
4 changes: 4 additions & 0 deletions release_build_files/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,10 @@ workflow use only during the development of your app, not for publicly shipping
code.

## Release Notes
### Upcoming
- Changes
- Remote Config (Desktop): Updated Flatbuffers to latest version to support buffer verification. Added flexbuffer integrity checks before parsing to prevent crashes on invalid/malformed data.

### 13.9.0
- Changes
- General (Android): Update to Firebase Android BoM version 34.15.0.
Expand Down
6 changes: 6 additions & 0 deletions remote_config/src/desktop/config_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ std::string NamespacedConfigData::Serialize() const {
void NamespacedConfigData::Deserialize(const std::string& buffer) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.data());
size_t size = buffer.size();
if (!flexbuffers::VerifyBuffer(data, size)) {
return;
}
auto struct_map = flexbuffers::GetRoot(data, size).AsMap();
flexbuffers::Map ns_config_map = struct_map["config_"].AsMap();
for (int i = 0, in = ns_config_map.size(); i < in; ++i) {
Expand Down Expand Up @@ -144,6 +147,9 @@ std::string LayeredConfigs::Serialize() const {
void LayeredConfigs::Deserialize(const std::string& buffer) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.data());
size_t size = buffer.size();
if (!flexbuffers::VerifyBuffer(data, size)) {
return;
}
auto struct_map = flexbuffers::GetRoot(data, size).AsMap();
fetched.Deserialize(struct_map["fetched"].AsString().str());
active.Deserialize(struct_map["active"].AsString().str());
Expand Down
25 changes: 21 additions & 4 deletions remote_config/src/desktop/file_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ namespace internal {

RemoteConfigFileManager::RemoteConfigFileManager(const std::string& filename,
const firebase::App& app) {
const char* package_name = app.options().package_name();
std::string app_data_prefix =
std::string(app.options().package_name()) + "/" + app.name();
std::string file_path =
AppDataDir(app_data_prefix.c_str(), /*should_create=*/true) + "/" +
filename;
std::string(package_name ? package_name : "") + "/remote_config";
Comment on lines +39 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

When package_name is null or empty, app_data_prefix is constructed as "/remote_config", which introduces an unintended leading slash. When package_name is present, it is constructed as "package_name/remote_config" (no leading slash).

This inconsistency can cause path resolution issues in AppDataDir (e.g., treating it as an absolute path or causing double slashes). We should ensure that no leading slash is present when package_name is empty or null.

Suggested change
const char* package_name = app.options().package_name();
std::string app_data_prefix =
std::string(app.options().package_name()) + "/" + app.name();
std::string file_path =
AppDataDir(app_data_prefix.c_str(), /*should_create=*/true) + "/" +
filename;
std::string(package_name ? package_name : "") + "/remote_config";
const char* package_name = app.options().package_name();
std::string app_data_prefix =
(package_name && package_name[0] != '\0')
? std::string(package_name) + "/remote_config"
: "remote_config";

std::string error;
std::string app_dir =
AppDataDir(app_data_prefix.c_str(), /*should_create=*/true, &error);
std::string file_path;
if (error.empty() && !app_dir.empty()) {
file_path = app_dir + "/" + app.name() + "_" + filename;
}
#if FIREBASE_PLATFORM_WINDOWS
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> utf8_to_wstring;
file_path_ = utf8_to_wstring.from_bytes(file_path);
Expand All @@ -50,16 +55,28 @@ RemoteConfigFileManager::RemoteConfigFileManager(const std::string& filename,
}

bool RemoteConfigFileManager::Load(LayeredConfigs* configs) const {
if (file_path_.empty()) {
return false;
}
std::fstream input(file_path_, std::ios::in | std::ios::binary);
if (!input) {
return false;
}
std::stringstream ss;
ss << input.rdbuf();
configs->Deserialize(ss.str());
return true;
}

bool RemoteConfigFileManager::Save(const LayeredConfigs& configs) const {
if (file_path_.empty()) {
return false;
}
std::string buffer = configs.Serialize();
std::fstream output(file_path_, std::ios::out | std::ios::binary);
if (!output) {
return false;
}
output.write(buffer.c_str(), buffer.size());
return true;
}
Expand Down
20 changes: 19 additions & 1 deletion remote_config/src/desktop/metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

#include "remote_config/src/desktop/metadata.h"

#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <map>
#include <string>
Comment thread
AustinBenoit marked this conversation as resolved.

Expand Down Expand Up @@ -59,6 +62,9 @@ std::string RemoteConfigMetadata::Serialize() const {
void RemoteConfigMetadata::Deserialize(const std::string& buffer) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.data());
size_t size = buffer.size();
if (!flexbuffers::VerifyBuffer(data, size)) {
return;
}
auto struct_map = flexbuffers::GetRoot(data, size).AsMap();

flexbuffers::Map info = struct_map["info"].AsMap();
Expand All @@ -76,7 +82,19 @@ void RemoteConfigMetadata::Deserialize(const std::string& buffer) {
settings_.clear();
flexbuffers::Map settings = struct_map["settings"].AsMap();
for (int i = 0, n = settings.size(); i < n; ++i) {
int int_key = std::stoi(settings.Keys()[i].AsKey());
const char* key_str = settings.Keys()[i].AsKey();
if (!key_str) continue;
char* endptr = nullptr;
errno = 0;
long raw_key = std::strtol(key_str, &endptr, 10);
if (endptr == key_str || *endptr != '\0' || errno == ERANGE) {
continue;
}
if (raw_key < std::numeric_limits<int>::min() ||
raw_key > std::numeric_limits<int>::max()) {
continue;
}
Comment thread
AustinBenoit marked this conversation as resolved.
int int_key = static_cast<int>(raw_key);
settings_[static_cast<ConfigSetting>(int_key)] =
settings.Values()[i].AsString().c_str();
}
Expand Down
4 changes: 4 additions & 0 deletions remote_config/src/desktop/remote_config_response.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ Variant FlexbufferToVariant(const flexbuffers::Reference& ref) {
case flexbuffers::FBT_BLOB:
LogError("Flexbuffers containing blobs are not supported.");
break;
case flexbuffers::FBT_MAX_TYPE:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;
Comment on lines +84 to +87

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using case flexbuffers::FBT_MAX_TYPE: only handles the specific sentinel value FBT_MAX_TYPE. If the flexbuffer is corrupted or contains an invalid/unknown type ID not covered by the switch, it will bypass this check entirely and silently return Variant::Null().

Using a default: case instead is much more robust as it will catch FBT_MAX_TYPE as well as any other unknown or corrupted type values.

Suggested change
case flexbuffers::FBT_MAX_TYPE:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;
default:
LogError("Unknown or unsupported flexbuffer type: %d",
static_cast<int>(ref.GetType()));
break;

}
return Variant::Null();
}
Expand Down
35 changes: 35 additions & 0 deletions scripts/git/patches/flatbuffers/0001-fix-error-macro.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
From: Antigravity <antigravity@google.com>
Subject: Patch flatbuffers to resolve ERROR macro conflict on Windows

Workaround for conflict between ProtoIdGapAction::ERROR in idl.h and the
Windows global ERROR macro defined in wingdi.h/windows.h.

See Flatbuffers Issue: https://github.com/google/flatbuffers/issues/8483

diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index 95fda8c2..6da1c6e3 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -17,6 +17,12 @@
#ifndef FLATBUFFERS_IDL_H_
#define FLATBUFFERS_IDL_H_

+#ifdef ERROR
+#pragma push_macro("ERROR")
+#undef ERROR
+#define FLATBUFFERS_POP_ERROR_MACRO
+#endif
+
#include <algorithm>
#include <functional>
#include <map>
@@ -1325,4 +1331,9 @@ extern bool GenerateTSGRPC(const Parser& parser, const std::string& path,
const std::string& file_name);
} // namespace flatbuffers

+#ifdef FLATBUFFERS_POP_ERROR_MACRO
+#pragma pop_macro("ERROR")
+#undef FLATBUFFERS_POP_ERROR_MACRO
+#endif
+
#endif // FLATBUFFERS_IDL_H_
Loading