Merged PR 782444: Refactor Linux sandbox to use new Create/ReportAccess functions in BxlObserver

Reinstate the change reverted by !781882 after the fixes in !782414
This commit is contained in:
Marcelo Lynch 🧉 2024-05-09 21:09:15 +00:00
Родитель ea58784fe6
Коммит 027a61067f
4 изменённых файлов: 646 добавлений и 399 удалений

Просмотреть файл

@ -634,26 +634,26 @@ namespace Test.BuildXL.Processes
public void CallTestmkdir()
{
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testdirectory")));
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testdirectory")));
}
[Fact]
public void CallTestmkdirat()
{
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testdirectory")));
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testdirectory")));
}
public void CallTestmknod()
{
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testfile")));
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testfile")));
}
public void CallTestmknodat()
{
var result = RunNativeTest(GetNativeTestName(MethodBase.GetCurrentMethod().Name));
AssertLogContains(GetRegex("report_create", Path.Combine(result.rootDirectory, "testfile")));
AssertLogContains(GetRegex("ReportCreate", Path.Combine(result.rootDirectory, "testfile")));
}
[Fact]

Просмотреть файл

@ -659,9 +659,20 @@ void BxlObserver::report_exec(const char *syscallName, const char *procName, con
{
if (IsMonitoringChildProcesses())
{
// first report 'procName' as is (without trying to resolve it) to ensure that a process name is reported before anything else
report_access(syscallName, ES_EVENT_TYPE_NOTIFY_EXEC, procName, empty_str_, mode, error, /* checkCache */ true, associatedPid);
report_access(syscallName, ES_EVENT_TYPE_NOTIFY_EXEC, file, mode, /*flags*/ 0, error, /* checkCache */ true, associatedPid);
if (associatedPid == 0)
{
associatedPid = getpid();
}
auto event = buildxl::linux::SandboxEvent::AbsolutePathSandboxEvent(
/* event_type */ ES_EVENT_TYPE_NOTIFY_EXEC,
/* pid */ associatedPid,
/* error */ error,
/* src_path */ file);
event.SetMode(mode);
CreateAndReportAccess(syscallName, event);
report_exec_args(associatedPid);
}
}
@ -728,249 +739,12 @@ void BxlObserver::report_exec_args(pid_t pid, const char *args) {
}
}
void BxlObserver::report_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, mode_t mode, int error, bool checkCache, pid_t associatedPid)
{
if (reportPath == nullptr || secondPath == nullptr)
{
// If we're given null paths, we can't really build a report. Just return.
LOG_DEBUG("Can't report an access for syscall %s with a null path. reportPath = %p, secondPath %p", syscallName, reportPath, secondPath);
return;
}
report_access_internal(syscallName, eventType, reportPath, secondPath, mode, error, checkCache, associatedPid);
}
void BxlObserver::report_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, mode_t mode, int error, bool checkCache, pid_t associatedPid)
{
AccessReportGroup report;
create_access_internal(syscallName, eventType, reportPath, secondPath, report, mode, checkCache, associatedPid);
report.SetErrno(error);
SendReport(report);
}
AccessCheckResult BxlObserver::create_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode, bool checkCache, pid_t associatedPid)
{
if (reportPath == nullptr || secondPath == nullptr)
{
LOG_DEBUG("Can't create an access for syscall %s with a null path. reportPath = %p, secondPath %p", syscallName, reportPath, secondPath);
return sNotChecked;
}
return create_access_internal(syscallName, eventType, reportPath, secondPath, reportGroup, mode, checkCache, associatedPid);
}
AccessCheckResult BxlObserver::create_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode, bool checkCache, pid_t associatedPid)
{
secondPath = secondPath == nullptr ? empty_str_ : secondPath;
if (checkCache && IsCacheHit(eventType, std::move(std::string(reportPath)), std::move(std::string(secondPath))))
{
return sNotChecked;
}
if (mode == 0)
{
// Mode hasn't been computed yet. Let's do it here.
mode = get_mode(reportPath);
}
// If this file descriptor is a non-file (e.g., a pipe, or socket, etc.) then we don't care about it
if (is_non_file(mode))
{
return sNotChecked;
}
std::string execPath = eventType == ES_EVENT_TYPE_NOTIFY_EXEC
? std::string(reportPath)
: std::string(progFullPath_);
IOEvent event(associatedPid == 0 ? getpid() : associatedPid, 0, getppid(), eventType, ES_ACTION_TYPE_NOTIFY, std::move(std::string(reportPath)), std::move(std::string(secondPath)), std::move(execPath), mode, false);
return create_access(syscallName, event, reportGroup, /* checkCache */ false /* because already checked cache above */);
}
void BxlObserver::report_access(const char *syscallName, IOEvent &event, bool checkCache)
{
AccessReportGroup report;
create_access(syscallName, event, report, checkCache);
SendReport(report);
}
AccessCheckResult BxlObserver::create_access(const char *syscallName, IOEvent &event, AccessReportGroup &reportGroup, bool checkCache)
{
es_event_type_t eventType = event.GetEventType();
if (checkCache && IsCacheHit(eventType, event.GetSrcPath(), event.GetDstPath()))
{
return sNotChecked;
}
AccessCheckResult result = sNotChecked;
pid_t pid = event.GetPid() == 0 ? getpid() : event.GetPid();
bool accessShouldBeBlocked = false;
if (IsEnabled(pid))
{
IOHandler handler(sandbox_);
handler.SetProcess(process_);
result = handler.CheckAccessAndBuildReport(event, reportGroup);
accessShouldBeBlocked = result.ShouldDenyAccess() && IsFailingUnexpectedAccesses();
if (!accessShouldBeBlocked)
{
// This access won't be blocked, so let's cache it.
// We populate cache even if checkCache is false, but this should be ok.
// We cache event types that are always a miss in IsCacheHit, but this also shoulld be fine.
CheckCache(eventType, event.GetSrcPath(), /* addEntryIfMissing */ true);
}
}
LOG_DEBUG("(( %10s:%2d )) %s %s%s", syscallName, event.GetEventType(), event.GetEventPath(),
!result.ShouldReport() ? "[Ignored]" : result.ShouldDenyAccess() ? "[Denied]" : "[Allowed]",
accessShouldBeBlocked ? "[Blocked]" : "");
return result;
}
void BxlObserver::report_access(const char *syscallName, es_event_type_t eventType, const char *pathname, mode_t mode, int flags, int error, bool checkCache, pid_t associatedPid)
{
// If the path is null or if we can't normalize it, we have no meaningful way of reporting this access
if (pathname == nullptr)
{
LOG_DEBUG("Can't report an access for syscall %s with a null path.", syscallName);
return;
}
auto normalized = normalize_path(pathname, flags, associatedPid);
if (normalized.length() == 0)
{
LOG_DEBUG("Couldn't normalize path %s", pathname);
return;
}
report_access_internal(syscallName, eventType, normalized.c_str(), /*secondPath*/ nullptr , mode, error, checkCache, associatedPid);
}
AccessCheckResult BxlObserver::create_access(const char *syscallName, es_event_type_t eventType, const char *pathname, AccessReportGroup &reportGroup, mode_t mode, int flags, bool checkCache, pid_t associatedPid)
{
// If the path is null or if we can't normalize it, we have no meaningful way of reporting this access
if (pathname == nullptr)
{
return sNotChecked;
}
auto normalized = normalize_path(pathname, flags, associatedPid);
if (normalized.length() == 0)
{
return sNotChecked;
}
return create_access_internal(syscallName, eventType, normalized.c_str(), /* secondPath */ nullptr, reportGroup, mode, checkCache, associatedPid);
}
void BxlObserver::report_access_fd(const char *syscallName, es_event_type_t eventType, int fd, int error, pid_t associatedPid)
{
AccessReportGroup report;
create_access_fd(syscallName, eventType, fd, report, associatedPid);
report.SetErrno(error);
SendReport(report);
}
AccessCheckResult BxlObserver::create_access_fd(const char *syscallName, es_event_type_t eventType, int fd, AccessReportGroup &report, pid_t associatedPid)
{
mode_t mode = get_mode(fd);
// If this file descriptor is a non-file (e.g., a pipe, or socket, etc.) then we don't care about it
if (is_non_file(mode))
{
return sNotChecked;
}
std::string fullpath = fd_to_path(fd, associatedPid);
// Only reports when fd_to_path succeeded.
return fullpath.length() > 0
? create_access_internal(syscallName, eventType, fullpath.c_str(), /* secondPath */ nullptr, report, mode, /* checkCache */ true, associatedPid)
: sNotChecked;
}
bool BxlObserver::is_non_file(const mode_t mode)
{
// Observe we don't care about block devices here. It is unlikely that we'll support them e2e, this is just an FYI.
return mode != 0 && !S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode);
}
AccessCheckResult BxlObserver::create_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, AccessReportGroup &report, int flags, bool getModeWithFd, pid_t associatedPid)
{
if (pathname == nullptr)
{
LOG_DEBUG("Can't create an access for syscall %s with a null path.", syscallName);
return sNotChecked;
}
if (pathname[0] == '/')
{
return create_access(syscallName, eventType, pathname, report, /* mode */0, flags, /* checkCache */ true, associatedPid);
}
char fullpath[PATH_MAX] = {0};
ssize_t len = 0;
mode_t mode = 0;
if (dirfd == AT_FDCWD)
{
if (!getcurrentworkingdirectory(fullpath, PATH_MAX, associatedPid))
{
return sNotChecked;
}
len = strlen(fullpath);
}
else
{
std::string dirPath;
// If getModeWithFd is set, then we can call get_mode directly with the file descriptor instead of a path
// If false, then use the provided associatedPid to convert the fd to a path and the get_mode on the path
if (getModeWithFd)
{
mode = get_mode(dirfd);
}
else
{
dirPath = fd_to_path(dirfd, associatedPid);
mode = get_mode(dirPath.c_str());
}
// If this file descriptor is a non-file (e.g., a pipe, or socket, etc.) then we don't care about it
if (is_non_file(mode))
{
return sNotChecked;
}
if (dirPath.empty())
{
dirPath = fd_to_path(dirfd);
}
len = dirPath.length();
strcpy(fullpath, dirPath.c_str());
}
if (len <= 0)
{
_fatal("Could not get path for fd %d; errno: %d", dirfd, errno);
}
snprintf(&fullpath[len], PATH_MAX - len, "/%s", pathname);
return create_access(syscallName, eventType, fullpath, report, mode, flags, /* checkCache */ true, associatedPid);
}
void BxlObserver::report_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, int flags, bool getModeWithFd, pid_t associatedPid, int error)
{
AccessReportGroup report;
create_access_at(syscallName, eventType, dirfd, pathname, report, flags, getModeWithFd, associatedPid);
report.SetErrno(error);
SendReport(report);
}
void BxlObserver::report_firstAllowWriteCheck(const char *fullPath)
{
mode_t mode = get_mode(fullPath);
@ -1447,7 +1221,14 @@ void BxlObserver::resolve_path(char *fullpath, bool followFinalSymlink, pid_t as
*pFullpath = '\0';
// break if the same symlink has already been visited (breaks symlink loops)
if (!visited.insert(fullpath).second) break;
report_access_internal("_readlink", ES_EVENT_TYPE_NOTIFY_READLINK, fullpath, /* secondPath */ (const char *)nullptr, /* mode */ 0, /* error */ 0, /* checkCache */ true, associatedPid);
auto event = buildxl::linux::SandboxEvent::AbsolutePathSandboxEvent(
/* event_type */ ES_EVENT_TYPE_NOTIFY_READLINK,
/* pid */ associatedPid,
/* error */ 0,
/* src_path */ fullpath);
CreateAndReportAccess("_readlink", event);
*pFullpath = ch;
// append the rest of the original path to the readlink target

Просмотреть файл

@ -246,8 +246,6 @@ private:
bool IsCacheHit(es_event_type_t event, const string &path, const string &secondPath);
bool CheckCache(es_event_type_t event, const string &path, bool addEntryIfMissing);
char** ensure_env_value_with_log(char *const envp[], char const *envName, const char *envValue);
void report_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath = nullptr, mode_t mode = 0, int error = 0, bool checkCache = true, pid_t associatedPid = 0);
AccessCheckResult create_access_internal(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode = 0, bool checkCache = true, pid_t associatedPid = 0);
ssize_t read_path_for_fd(int fd, char *buf, size_t bufsiz, pid_t associatedPid = 0);
bool IsMonitoringChildProcesses() const { return !pip_ || CheckMonitorChildProcesses(pip_->GetFamFlags()); }
@ -293,7 +291,7 @@ private:
* If possible, resolves relative or file descriptor paths to absolute paths in a SandboxEvent, and returns true.
* This might not be possible if the event is a file-descriptor specified event and the file descriptors
* do not correspond to real paths. In this case, the function returns false and the event is unmodified.
* In both cases, the mode of the source path of the event is updated
* In both cases, the mode of the source path of the event is updated.
*/
bool ResolveEventPaths(buildxl::linux::SandboxEvent& event);
@ -382,27 +380,6 @@ public:
*/
void CreateAndReportAccess(const char *syscall_name, buildxl::linux::SandboxEvent& event, bool check_cache = true);
// The following functions create an access report and performs an access check. They do not report the created access to managed BuildXL.
// The created access report is returned as an out param in the given 'report' param. The returned report is ready to be sent with the exception of
// setting the operation error. In operations where the error is reported back, the typical flow is creating the report, performing the operation,
// setting errno in the report and sending out the report.
AccessCheckResult create_access(const char *syscallName, IOEvent &event, AccessReportGroup &report, bool checkCache = true);
// In this method (and immediately below) 'mode' is provided on a best effort basis. If 0 is passed for mode, it will be
// explicitly computed
// NOTE: The associatedPid value for the create_access and report_access functions below take a default value of 0 because they are only set by the ptrace sandbox.
// If a value of 0 is set for associatedPid, then it is safe to assume that the report is from the interpose sandbox.
AccessCheckResult create_access(const char *syscallName, es_event_type_t eventType, const char *pathname, AccessReportGroup &report, mode_t mode = 0, int oflags = 0, bool checkCache = true, pid_t associatedPid = 0);
AccessCheckResult create_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, AccessReportGroup &reportGroup, mode_t mode = 0, bool checkCache = true, pid_t associatedPid = 0);
AccessCheckResult create_access_fd(const char *syscallName, es_event_type_t eventType, int fd, AccessReportGroup &reportGroup, pid_t associatedPid = 0);
AccessCheckResult create_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, AccessReportGroup &reportGroup, int oflags = 0, bool getModeWithFd = true, pid_t associatedPid = 0);
// The following functions are the create_* equivalent of the ones above but the access is reported to managed BuildXL
void report_access(const char *syscallName, IOEvent &event, bool checkCache = true);
void report_access(const char *syscallName, es_event_type_t eventType, const char *pathname, mode_t mode = 0, int oflags = 0, int error = 0, bool checkCache = true, pid_t associatedPid = 0);
void report_access(const char *syscallName, es_event_type_t eventType, const char *reportPath, const char *secondPath, mode_t mode = 0, int error = 0, bool checkCache = true, pid_t associatedPid = 0);
void report_access_fd(const char *syscallName, es_event_type_t eventType, int fd, int error, pid_t associatedPid = 0);
void report_access_at(const char *syscallName, es_event_type_t eventType, int dirfd, const char *pathname, int oflags, bool getModeWithFd = true, pid_t associatedPid = 0, int error = 0);
// Send a special message to managed code if the policy to override allowed writes based on file existence is set
// and the write is allowed by policy
void report_firstAllowWriteCheck(const char *fullPath);

Разница между файлами не показана из-за своего большого размера Загрузить разницу