kernel: support proc:{thread_fd}/<sub-handle> path format

The relibc fork's pthread_setname_np / pthread_getname_np /
pthread_setaffinity_np / pthread_getaffinity_np and
mutex_owner_id_is_live all use the path format
'proc:{thread_fd}/<sub-handle>' (e.g.
'proc:123/name' or 'proc:123/sched-affinity') via a single
Sys::open() call.

Previously the proc scheme's OpenTy::Auth handler in
src/scheme/proc.rs only recognized 'new-context' and
'cur-context' as literal strings, so '123/name' would
hit the _ => ENOENT arm and the relibc calls would
fail with ENOENT at runtime.

Fix: add a third arm in OpenTy::Auth that splits the
operation string on the first '/', parses the prefix as
a numeric context id, looks up the corresponding
ContextHandle in the HANDLES map, and recursively
dispatches to openat_context with the suffix as the
sub-handle path. This makes 'proc:123/name' resolve to
the same handle chain that 'dup(123, "name")' would
have produced.

The recursive call is safe because openat_context
doesn't depend on the Authority-only state. The
HANDLES map is read-locked; we drop the lock before
the recursive call by scoping the handles variable.

Discovered by Oracle review of Phase 0c patches
(Issue 1). The bug was latent in the original
P5-proc-setschedpolicy patch (before Phase 0c) and
survived because the relibc code paths were never
exercised at runtime.
This commit is contained in:
2026-07-02 10:53:52 +03:00
parent d37b421cb3
commit d41d0aa728
+58 -20
View File
@@ -309,27 +309,65 @@ impl ProcScheme {
}
OpenTy::Auth => {
extern "C" fn ret() {}
let context = match operation_str.ok_or(Error::new(ENOENT))? {
"new-context" => {
let id = NonZeroUsize::new(NEXT_ID.fetch_add(1, Ordering::Relaxed))
.ok_or(Error::new(EMFILE))?;
let context = context::spawn(true, Some(id), ret, token)?;
{
let parent_groups =
context::current().read(token.token()).groups.clone();
context.write(token.token()).groups = parent_groups;
}
HANDLES.write(token.token()).insert(
id.get(),
Handle {
context,
kind: ContextHandle::OpenViaDup,
},
);
return Ok((id.get(), InternalFlags::empty()));
let operation_str = operation_str.ok_or(Error::new(ENOENT))?;
// The relibc fork's pthread_setname_np / getname_np /
// setaffinity_np / getaffinity_np all use the path
// format "proc:{thread_fd}/<sub-handle>" via a single
// Sys::open() call. To support this, we need to parse
// the leading numeric prefix as a context id and the
// remaining suffix as the sub-handle path. The
// existing "new-context" and "cur-context" arms are
// kept for the "literal" paths that don't have a
// numeric prefix.
let context = if operation_str == "new-context" {
let id = NonZeroUsize::new(NEXT_ID.fetch_add(1, Ordering::Relaxed))
.ok_or(Error::new(EMFILE))?;
let context = context::spawn(true, Some(id), ret, token)?;
{
let parent_groups =
context::current().read(token.token()).groups.clone();
context.write(token.token()).groups = parent_groups;
}
"cur-context" => context::current(),
_ => return Err(Error::new(ENOENT)),
HANDLES.write(token.token()).insert(
id.get(),
Handle {
context: context.clone(),
kind: ContextHandle::OpenViaDup,
},
);
return Ok((id.get(), InternalFlags::empty()));
} else if operation_str == "cur-context" {
context::current()
} else if let Some(slash) = operation_str.find('/') {
// Path format "<thread_fd>/<sub-handle>" used by
// relibc's pthread_setname_np / getname_np /
// setaffinity_np / getaffinity_np. Split on the
// first '/' to get the numeric context id and
// the sub-handle name. Look up the context by id
// in the HANDLES map.
let id_str = &operation_str[..slash];
let sub_path = &operation_str[slash + 1..];
let id = id_str.parse::<usize>().map_err(|_| Error::new(EINVAL))?;
let context = {
let handles = HANDLES.read(token.token());
let handle = handles.get(&id).ok_or(Error::new(ENOENT))?;
if !matches!(
handle.kind,
ContextHandle::OpenViaDup | ContextHandle::Authority
) {
return Err(Error::new(ENOENT));
}
handle.context.clone()
};
// Re-call openat_context with the sub-path. This
// is a recursive open_inner which works because
// openat_context doesn't depend on the open
// path's Authority state.
return self
.open_inner(OpenTy::Ctxt(context), Some(sub_path), _flags, token)
.map(|(id, flags)| (id, flags));
} else {
return Err(Error::new(ENOENT));
};
(