// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/mac/metrics.h"
#import <Foundation/Foundation.h>
#include <Security/Security.h>
#include <sys/attr.h>
#include <sys/vnode.h>
#include <unistd.h>
#include "base/containers/fixed_flat_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/upgrade_detector/upgrade_detector.h"
namespace {
mac_metrics::FileSystemType VolumeTagToFileSystemType(enum vtagtype tag) {
static constexpr auto map =
base::MakeFixedFlatMap<enum vtagtype, mac_metrics::FileSystemType>({
{VT_HFS, mac_metrics::FileSystemType::kHFS},
{VT_APFS, mac_metrics::FileSystemType::kAPFS},
});
const auto it = map.find(tag);
return it != map.end() ? it->second : mac_metrics::FileSystemType::kUnknown;
}
void RecordAppFileSystemTypeUsingVolumeTag(enum vtagtype tag) {
base::UmaHistogramEnumeration("Mac.AppFileSystemType",
VolumeTagToFileSystemType(tag));
}
void RecordAppUpgradeCodeSignatureValidationStatus(OSStatus status) {
// There are currently 83 possible code signing errSec values.
// https://github.com/apple-oss-distributions/Security/blob/Security-61040.80.10.0.1/OSX/libsecurity_codesigning/lib/CSCommon.h#L64
base::UmaHistogramSparse("Mac.AppUpgradeCodeSignatureValidationStatus",
status);
}
void RecordAppUpgradeCodeSignatureValidationImpl(base::OnceClosure closure) {
// SecCodeCheckValidity blocks on I/O, do the validation and metric recording
// on from background thread.
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
task_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](base::OnceClosure closure) {
SecCodeRef self_code = nullptr;
if (SecCodeCopySelf(kSecCSDefaultFlags, &self_code) !=
errSecSuccess) {
std::move(closure).Run();
return;
}
// Ignoring revocation status with the kSecCSNoNetworkAccess.
RecordAppUpgradeCodeSignatureValidationStatus(SecCodeCheckValidity(
self_code, kSecCSNoNetworkAccess, nullptr));
if (self_code) {
CFRelease(self_code);
}
std::move(closure).Run();
},
std::move(closure)));
}
struct alignas(4) AttributeBuffer {
uint32_t length;
enum vtagtype tag;
} __attribute__((packed));
} // namespace
namespace mac_metrics {
Metrics::Metrics() {
UpgradeDetector::GetInstance()->AddObserver(this);
}
Metrics::~Metrics() {
UpgradeDetector::GetInstance()->RemoveObserver(this);
}
void Metrics::RecordAppFileSystemType() {
const char* path =
NSProcessInfo.processInfo.arguments.firstObject.fileSystemRepresentation;
struct attrlist attr_list = {
.bitmapcount = ATTR_BIT_MAP_COUNT, // default
.commonattr = ATTR_CMN_OBJTAG // get the file system type
};
struct AttributeBuffer buff;
// Using getattrlist instead of statfs. ATTR_CMN_OBJTAG from getattrlist is
// the only value needed, which should be faster to get than the whole statfs
// struct. Additionally the statfs field f_type does not seem to map to any
// public enum of file system types. According to man 2 getattrlist it is not
// a useful value. The f_fstypename field could be used but adds the
// additional burden of handling strings.
if (getattrlist(path, &attr_list, &buff, sizeof(buff), 0) != 0) {
// Record FileSystemType::kUnknown if there is a failure with getattrlist.
RecordAppFileSystemTypeUsingVolumeTag(VT_NON);
return;
}
DCHECK_GE(sizeof(buff), buff.length);
RecordAppFileSystemTypeUsingVolumeTag(buff.tag);
}
void Metrics::OnUpgradeRecommended() {
// By default OnUpgradeRecommended is called multiple times over the course
// of 7 days, the first being 1 hour after the update has been staged. Record
// the metric once and ignore all other calls.
static bool once = []() {
RecordAppUpgradeCodeSignatureValidationImpl(base::DoNothing());
return true;
}();
DCHECK(once);
}
void Metrics::RecordAppUpgradeCodeSignatureValidation(
base::OnceClosure closure) {
RecordAppUpgradeCodeSignatureValidationImpl(std::move(closure));
}
} // namespace mac_metrics