[go: nahoru, domu]

blob: c8f6c4dcc71f940be9a2f132245bdd0539e26dd2 [file] [log] [blame]
// Copyright 2023 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/updater/lock.h"
#include <aio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <utility>
#include "base/files/file.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/updater_scope.h"
namespace {
constexpr char kSharedMemNamePrefix[] = "/" PRODUCT_FULLNAME_STRING;
constexpr char kSharedMemNameSuffix[] = ".lock";
} // namespace
namespace updater {
class ScopedLockImpl {
public:
ScopedLockImpl(const ScopedLockImpl&) = delete;
ScopedLockImpl& operator=(const ScopedLockImpl&) = delete;
~ScopedLockImpl();
static std::unique_ptr<ScopedLockImpl> TryCreate(const std::string& id,
UpdaterScope scope,
base::TimeDelta timeout);
private:
ScopedLockImpl(base::raw_ptr<pthread_mutex_t> mutex, int shm_fd)
: mutex_(mutex), shm_fd_(shm_fd) {}
base::raw_ptr<pthread_mutex_t> mutex_;
int shm_fd_;
};
std::unique_ptr<ScopedLockImpl> ScopedLockImpl::TryCreate(
const std::string& id,
UpdaterScope scope,
base::TimeDelta timeout) {
bool should_init_mutex = false;
const std::string shared_mem_name(
base::StrCat({kSharedMemNamePrefix, id, UpdaterScopeToString(scope),
kSharedMemNameSuffix}));
int shm_fd = shm_open(shared_mem_name.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
if (shm_fd < 0 && errno == ENOENT) {
shm_fd =
shm_open(shared_mem_name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
should_init_mutex = true;
}
if (shm_fd < 0) {
return nullptr;
}
base::stat_wrapper_t shm_stat;
if (base::File::Fstat(shm_fd, &shm_stat) < 0) {
PLOG(ERROR) << "Cannot stat shared memory " << shared_mem_name;
return nullptr;
}
if (shm_stat.st_uid != getuid() ||
(shm_stat.st_mode & 0777) != (S_IRUSR | S_IWUSR)) {
LOG(ERROR) << "Refusing to use shared memory region " << shared_mem_name
<< " with incorrect permissions";
return nullptr;
}
if (ftruncate(shm_fd, sizeof(pthread_mutex_t))) {
return nullptr;
}
void* addr = mmap(nullptr, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE,
MAP_SHARED, shm_fd, 0);
if (addr == MAP_FAILED) {
return nullptr;
}
pthread_mutex_t* mutex = static_cast<pthread_mutex_t*>(addr);
// Note that the mutex is configured with the "robust" attribute. This ensures
// that even if a process crashes while holding the mutex, the mutex is
// recoverable.
if (should_init_mutex) {
pthread_mutexattr_t attr = {};
if (pthread_mutexattr_init(&attr) ||
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) ||
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) ||
pthread_mutex_init(mutex, &attr)) {
munmap(addr, sizeof(pthread_mutex_t));
return nullptr;
}
}
const base::Time start = base::Time::NowFromSystemTime();
do {
switch (pthread_mutex_trylock(mutex)) {
case EOWNERDEAD: {
// A process holding the mutex died, try to recover it.
if (pthread_mutex_consistent(mutex)) {
return nullptr;
}
// The mutex is restored. It is in the locked state.
[[fallthrough]];
}
case 0: {
// The lock was acquired.
return base::WrapUnique<ScopedLockImpl>(
new ScopedLockImpl(mutex, shm_fd));
}
case EBUSY: {
// The mutex is held by another process.
continue;
}
default: {
// An error occurred.
return nullptr;
}
}
} while (base::Time::NowFromSystemTime() - start < timeout);
// The lock was not acquired before the timeout.
return nullptr;
}
ScopedLockImpl::~ScopedLockImpl() {
// TODO(crbug.com/1370140): Fix shared memory leak.
if (mutex_) {
pthread_mutex_unlock(mutex_.get());
munmap(mutex_.get(), sizeof(pthread_mutex_t));
close(shm_fd_);
}
}
ScopedLock::ScopedLock(std::unique_ptr<ScopedLockImpl> impl)
: impl_(std::move(impl)) {}
ScopedLock::~ScopedLock() = default;
// static
std::unique_ptr<ScopedLock> ScopedLock::Create(const std::string& name,
UpdaterScope scope,
base::TimeDelta timeout) {
std::unique_ptr<ScopedLockImpl> impl =
ScopedLockImpl::TryCreate(name, scope, timeout);
return impl ? std::make_unique<ScopedLock>(std::move(impl)) : nullptr;
}
} // namespace updater