297 lines
8.5 KiB
C++
297 lines
8.5 KiB
C++
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
|
//
|
|
// Licensed 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.
|
|
|
|
#include "util/posix/process_info.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "base/files/file_path.h"
|
|
#include "base/logging.h"
|
|
#include "util/file/delimited_file_reader.h"
|
|
#include "util/file/file_reader.h"
|
|
#include "util/file/string_file.h"
|
|
#include "util/linux/proc_stat_reader.h"
|
|
#include "util/misc/lexing.h"
|
|
#include "util/misc/time.h"
|
|
|
|
namespace crashpad {
|
|
|
|
ProcessInfo::ProcessInfo()
|
|
: connection_(),
|
|
supplementary_groups_(),
|
|
start_time_(),
|
|
pid_(-1),
|
|
ppid_(-1),
|
|
uid_(-1),
|
|
euid_(-1),
|
|
suid_(-1),
|
|
gid_(-1),
|
|
egid_(-1),
|
|
sgid_(-1),
|
|
start_time_initialized_(),
|
|
initialized_() {}
|
|
|
|
ProcessInfo::~ProcessInfo() {}
|
|
|
|
bool ProcessInfo::InitializeWithPtrace(PtraceConnection* connection) {
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
DCHECK(connection);
|
|
|
|
connection_ = connection;
|
|
pid_ = connection->GetProcessID();
|
|
is_64_bit_ = connection->Is64Bit();
|
|
|
|
{
|
|
char path[32];
|
|
snprintf(path, sizeof(path), "/proc/%d/status", pid_);
|
|
std::string contents;
|
|
if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
|
|
return false;
|
|
}
|
|
StringFile status_file;
|
|
status_file.SetString(contents);
|
|
|
|
DelimitedFileReader status_file_line_reader(&status_file);
|
|
|
|
bool have_ppid = false;
|
|
bool have_uids = false;
|
|
bool have_gids = false;
|
|
bool have_groups = false;
|
|
std::string line;
|
|
DelimitedFileReader::Result result;
|
|
while ((result = status_file_line_reader.GetLine(&line)) ==
|
|
DelimitedFileReader::Result::kSuccess) {
|
|
if (line.back() != '\n') {
|
|
LOG(ERROR) << "format error: unterminated line at EOF";
|
|
return false;
|
|
}
|
|
|
|
bool understood_line = false;
|
|
const char* line_c = line.c_str();
|
|
if (AdvancePastPrefix(&line_c, "PPid:\t")) {
|
|
if (have_ppid) {
|
|
LOG(ERROR) << "format error: multiple PPid lines";
|
|
return false;
|
|
}
|
|
have_ppid = AdvancePastNumber(&line_c, &ppid_);
|
|
if (!have_ppid) {
|
|
LOG(ERROR) << "format error: unrecognized PPid format";
|
|
return false;
|
|
}
|
|
understood_line = true;
|
|
} else if (AdvancePastPrefix(&line_c, "Uid:\t")) {
|
|
if (have_uids) {
|
|
LOG(ERROR) << "format error: multiple Uid lines";
|
|
return false;
|
|
}
|
|
uid_t fsuid;
|
|
have_uids = AdvancePastNumber(&line_c, &uid_) &&
|
|
AdvancePastPrefix(&line_c, "\t") &&
|
|
AdvancePastNumber(&line_c, &euid_) &&
|
|
AdvancePastPrefix(&line_c, "\t") &&
|
|
AdvancePastNumber(&line_c, &suid_) &&
|
|
AdvancePastPrefix(&line_c, "\t") &&
|
|
AdvancePastNumber(&line_c, &fsuid);
|
|
if (!have_uids) {
|
|
LOG(ERROR) << "format error: unrecognized Uid format";
|
|
return false;
|
|
}
|
|
understood_line = true;
|
|
} else if (AdvancePastPrefix(&line_c, "Gid:\t")) {
|
|
if (have_gids) {
|
|
LOG(ERROR) << "format error: multiple Gid lines";
|
|
return false;
|
|
}
|
|
gid_t fsgid;
|
|
have_gids = AdvancePastNumber(&line_c, &gid_) &&
|
|
AdvancePastPrefix(&line_c, "\t") &&
|
|
AdvancePastNumber(&line_c, &egid_) &&
|
|
AdvancePastPrefix(&line_c, "\t") &&
|
|
AdvancePastNumber(&line_c, &sgid_) &&
|
|
AdvancePastPrefix(&line_c, "\t") &&
|
|
AdvancePastNumber(&line_c, &fsgid);
|
|
if (!have_gids) {
|
|
LOG(ERROR) << "format error: unrecognized Gid format";
|
|
return false;
|
|
}
|
|
understood_line = true;
|
|
} else if (AdvancePastPrefix(&line_c, "Groups:\t")) {
|
|
if (have_groups) {
|
|
LOG(ERROR) << "format error: multiple Groups lines";
|
|
return false;
|
|
}
|
|
if (!AdvancePastPrefix(&line_c, " ")) {
|
|
// In Linux 4.10, even an empty Groups: line has a trailing space.
|
|
gid_t group;
|
|
while (AdvancePastNumber(&line_c, &group)) {
|
|
supplementary_groups_.insert(group);
|
|
if (!AdvancePastPrefix(&line_c, " ")) {
|
|
LOG(ERROR) << "format error: unrecognized Groups format";
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
have_groups = true;
|
|
understood_line = true;
|
|
}
|
|
|
|
if (understood_line && line_c != &line.back()) {
|
|
LOG(ERROR) << "format error: unconsumed trailing data";
|
|
return false;
|
|
}
|
|
}
|
|
if (result != DelimitedFileReader::Result::kEndOfFile) {
|
|
return false;
|
|
}
|
|
if (!have_ppid || !have_uids || !have_gids || !have_groups) {
|
|
LOG(ERROR) << "format error: missing fields";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
return true;
|
|
}
|
|
|
|
pid_t ProcessInfo::ProcessID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return pid_;
|
|
}
|
|
|
|
pid_t ProcessInfo::ParentProcessID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return ppid_;
|
|
}
|
|
|
|
uid_t ProcessInfo::RealUserID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return uid_;
|
|
}
|
|
|
|
uid_t ProcessInfo::EffectiveUserID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return euid_;
|
|
}
|
|
|
|
uid_t ProcessInfo::SavedUserID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return suid_;
|
|
}
|
|
|
|
gid_t ProcessInfo::RealGroupID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return gid_;
|
|
}
|
|
|
|
gid_t ProcessInfo::EffectiveGroupID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return egid_;
|
|
}
|
|
|
|
gid_t ProcessInfo::SavedGroupID() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return sgid_;
|
|
}
|
|
|
|
std::set<gid_t> ProcessInfo::SupplementaryGroups() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return supplementary_groups_;
|
|
}
|
|
|
|
std::set<gid_t> ProcessInfo::AllGroups() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
std::set<gid_t> all_groups = SupplementaryGroups();
|
|
all_groups.insert(RealGroupID());
|
|
all_groups.insert(EffectiveGroupID());
|
|
all_groups.insert(SavedGroupID());
|
|
return all_groups;
|
|
}
|
|
|
|
bool ProcessInfo::DidChangePrivileges() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
// TODO(jperaza): Is this possible to determine?
|
|
return false;
|
|
}
|
|
|
|
bool ProcessInfo::Is64Bit() const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
return is_64_bit_;
|
|
}
|
|
|
|
bool ProcessInfo::StartTime(timeval* start_time) const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
if (start_time_initialized_.is_uninitialized()) {
|
|
start_time_initialized_.set_invalid();
|
|
ProcStatReader reader;
|
|
if (!reader.Initialize(connection_, pid_)) {
|
|
return false;
|
|
}
|
|
timespec boot_time_ts;
|
|
if (!GetBootTime(&boot_time_ts)) {
|
|
return false;
|
|
}
|
|
timeval boot_time;
|
|
TimespecToTimeval(boot_time_ts, &boot_time);
|
|
if (!reader.StartTime(boot_time, &start_time_)) {
|
|
return false;
|
|
}
|
|
start_time_initialized_.set_valid();
|
|
}
|
|
|
|
if (!start_time_initialized_.is_valid()) {
|
|
return false;
|
|
}
|
|
|
|
*start_time = start_time_;
|
|
return true;
|
|
}
|
|
|
|
bool ProcessInfo::Arguments(std::vector<std::string>* argv) const {
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
char path[32];
|
|
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid_);
|
|
std::string contents;
|
|
if (!connection_->ReadFileContents(base::FilePath(path), &contents)) {
|
|
return false;
|
|
}
|
|
StringFile cmdline_file;
|
|
cmdline_file.SetString(contents);
|
|
|
|
DelimitedFileReader cmdline_file_field_reader(&cmdline_file);
|
|
|
|
std::vector<std::string> local_argv;
|
|
std::string argument;
|
|
DelimitedFileReader::Result result;
|
|
while ((result = cmdline_file_field_reader.GetDelim('\0', &argument)) ==
|
|
DelimitedFileReader::Result::kSuccess) {
|
|
if (argument.back() != '\0') {
|
|
LOG(ERROR) << "format error";
|
|
return false;
|
|
}
|
|
argument.pop_back();
|
|
local_argv.push_back(argument);
|
|
}
|
|
if (result != DelimitedFileReader::Result::kEndOfFile) {
|
|
return false;
|
|
}
|
|
|
|
argv->swap(local_argv);
|
|
return true;
|
|
}
|
|
|
|
} // namespace crashpad
|