SERVER-115584 detect huge procfs files during FTDC collection (#47923)

Co-authored-by: Billy Donahue <BillyDonahue@users.noreply.github.com>
GitOrigin-RevId: 43dc25b39b217c41e6c3cc0ab37e69f60134db3f
This commit is contained in:
Cole Harbeck 2026-02-17 21:04:32 -05:00 committed by MongoDB Bot
parent dcc27418ff
commit 1de5943c67
7 changed files with 214 additions and 5 deletions

View File

@ -119,7 +119,10 @@ mongo_cc_library(
"//src/mongo/s:common_s",
"//src/mongo/util:processinfo",
] + select({
"@platforms//os:linux": ["//src/mongo/util:procparser"],
"@platforms//os:linux": [
"//src/mongo/util:procparser",
"//src/mongo/util:procparser_parameters",
],
"@platforms//os:windows": ["//src/mongo/util:perfctr_collect"],
"//conditions:default": [],
}),

View File

@ -584,14 +584,36 @@ mongo_cc_library(
name = "procparser",
srcs = [
"procparser.cpp",
"procparser.h",
],
hdrs = ["procparser.h"],
target_compatible_with = select({
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
":pcre_wrapper",
"//src/mongo/db:server_base",
],
)
idl_generator(
name = "procparser_parameters_gen",
src = "procparser_parameters.idl",
)
mongo_cc_library(
name = "procparser_parameters",
srcs = [
"procparser_parameters.cpp",
":procparser_parameters_gen",
],
hdrs = ["procparser_parameters.h"],
target_compatible_with = select({
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
":procparser",
"//src/mongo:base",
],
)

View File

@ -34,6 +34,7 @@
#include <array>
#include <cerrno>
#include <cstddef>
#include <fstream>
#include <istream>
#include <map>
#include <set>
@ -68,6 +69,7 @@
#include "mongo/bson/util/builder.h"
#include "mongo/config.h" // IWYU pragma: keep
#include "mongo/logv2/log.h"
#include "mongo/logv2/log_severity_suppressor.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/errno_util.h"
#include "mongo/util/pcre.h"
@ -116,6 +118,41 @@ const size_t kFileReadRetryCount = 5;
constexpr auto kSysBlockDeviceDirectoryName = "device";
/**
* Open and read a whole file, discarding data, just to get its size.
* Full read is needed for special files because stat will report 0 size.
*/
size_t fileByteCount(StringData filename) {
std::ifstream ifs(std::string{filename});
auto buf = std::make_unique_for_overwrite<std::array<char, kFileBufferSize>>();
size_t nr = 0;
while (ifs) {
ifs.read(buf->data(), buf->size());
nr += ifs.gcount();
}
return nr;
}
Status detectHugeFile(StringData filename, const BufBuilder& builder) {
const size_t sizeLimit = procparser::getProcFileSizeLimit();
if (static_cast<size_t>(builder.len()) < sizeLimit)
return Status::OK();
static logv2::SeveritySuppressor logSeverity(
Minutes{5}, logv2::LogSeverity::Warning(), logv2::LogSeverity::Debug(3));
if (auto sev = logSeverity(); shouldLog(MONGO_LOGV2_DEFAULT_COMPONENT, sev)) {
LOGV2_DEBUG(
11558400,
sev.toInt(),
"Huge file",
"filename"_attr = filename,
"byteCount"_attr = fileByteCount(filename),
"sizeLimit"_attr = static_cast<long long>(sizeLimit),
"partialContents"_attr =
StringData(builder.buf(), static_cast<size_t>(builder.len())).substr(0, 256));
}
return {ErrorCodes::FileStreamFailed, fmt::format("Huge file {:?}", filename)};
}
/**
* Read a file from disk as a string with a null-terminating byte using the POSIX file api.
*
@ -137,7 +174,7 @@ StatusWith<std::string> readFileAsString(StringData filename) {
ScopeGuard scopedGuard([fd] { close(fd); });
BufBuilder builder(kFileBufferSize);
std::array<char, kFileBufferSize> buf;
auto buf = std::make_unique_for_overwrite<std::array<char, kFileBufferSize>>();
ssize_t size_read = 0;
@ -148,7 +185,7 @@ StatusWith<std::string> readFileAsString(StringData filename) {
size_t retry = 0;
do {
size_read = read(fd, buf.data(), kFileBufferSize);
size_read = read(fd, buf->data(), buf->size());
if (size_read == -1) {
auto ec = lastPosixError();
@ -169,7 +206,9 @@ StatusWith<std::string> readFileAsString(StringData filename) {
} while (true);
if (size_read != 0) {
builder.appendBuf(buf.data(), size_read);
builder.appendBuf(buf->data(), size_read);
if (auto err = detectHugeFile(filename, builder); !err.isOK())
return err;
}
} while (size_read != 0);
@ -208,12 +247,22 @@ const char* const kDiskFields[] = {
const size_t kDiskFieldCount = std::extent<decltype(kDiskFields)>::value;
size_t procFileSizeLimit = size_t{1} * 1024 * 1024; // 1MiB
} // namespace
namespace procparser {
using StringSplitIterator = boost::split_iterator<StringData::const_iterator>;
void setProcFileSizeLimit(size_t limit) {
procFileSizeLimit = limit;
}
size_t getProcFileSizeLimit() {
return procFileSizeLimit;
}
/**
* This function is a generic function that passes in logic to parse a proc file. It looks for
* keys within the data param and captures the corresponding values. If no keys were found

View File

@ -266,5 +266,13 @@ Status parseProcPressure(StringData data, BSONObjBuilder* builder);
*/
Status parseProcPressureFile(StringData key, StringData filename, BSONObjBuilder* builder);
/**
* Procfs files such as the files parsed by this component should be just a small to moderate amount
* of text. To protect us from abnormally huge or even corrupted procfs files, we enforce a max size
* on them. The ability to adjust this limit is allowed as a safety measure for the safety measure.
*/
void setProcFileSizeLimit(size_t limit);
size_t getProcFileSizeLimit();
} // namespace procparser
} // namespace mongo

View File

@ -0,0 +1,41 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/util/procparser_parameters.h"
#include "mongo/util/procparser.h"
namespace mongo::procparser {
Status onUpdateProcFileSizeLimit(const long long& limit) {
setProcFileSizeLimit(limit);
return Status::OK();
}
} // namespace mongo::procparser

View File

@ -0,0 +1,45 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/base/status.h"
#include "mongo/util/modules.h"
#include <cstdint>
#include <boost/optional.hpp>
namespace mongo {
namespace procparser {
Status onUpdateProcFileSizeLimit(const long long& limit);
} // namespace procparser
} // namespace mongo

View File

@ -0,0 +1,41 @@
# Copyright (C) 2026-present MongoDB, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the Server Side Public License, version 1,
# as published by MongoDB, Inc.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Server Side Public License for more details.
#
# You should have received a copy of the Server Side Public License
# along with this program. If not, see
# <http://www.mongodb.com/licensing/server-side-public-license>.
#
# As a special exception, the copyright holders give permission to link the
# code of portions of this program with the OpenSSL library under certain
# conditions as described in each individual source file and distribute
# linked combinations including the program with the OpenSSL library. You
# must comply with the Server Side Public License in all respects for
# all of the code used other than as permitted herein. If you modify file(s)
# with this exception, you may extend this exception to your version of the
# file(s), but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version. If you delete this
# exception statement from all source files in the program, then also delete
# it in the license file.
#
global:
cpp_namespace: "mongo::procparser"
cpp_includes:
- "mongo/util/procparser_parameters.h"
server_parameters:
procFileSizeLimit:
description: "Server will not read procfs files larger than this limit"
set_at: ["startup"]
cpp_varname: "procFileSizeLimit"
cpp_vartype: long long
default: 1048576 # 1MiB
redact: false
on_update: onUpdateProcFileSizeLimit