From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9709F33FE0F for ; Fri, 17 Apr 2026 16:51:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.171 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776444706; cv=none; b=dIQGmeVFGxgn+SkjUoFDbpEE7b7bT/ZZ8x0jays2klsJ1G0sp7ciP2koOsGq63kFN/9NBDrx9nQsY9QIc8K84ddQljuAhH0SBki4F35urnQ3mCWno1mRGzGFzpm4dW/7peuXGpL77COCb5tokEv4gMRlbff9jajV2+d21Fh5+0Q= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776444706; c=relaxed/simple; bh=XQPvDOUBqGL5yLrbPZgix+AzB7PlZi0qZZ57sPKCqh0=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=BzXLfX9x1LkNDEdXZT3cadV6kj96l+ljDyWoYN9/OgJp2EeGbQjyrltJwUfFIZAZJ4QdW77mGM6eqGp9pt6BEX3yVoP1re7CFGZq313GUboAPfG1HPSyjBw545quuF6yo6slzqHCF8l4si+jkwGIHjj3sVINDDDdApaw5819EfI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iK/QAaUW; arc=none smtp.client-ip=209.85.128.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iK/QAaUW" Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-79ee5037d44so13707267b3.0 for ; Fri, 17 Apr 2026 09:51:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776444703; x=1777049503; darn=vger.kernel.org; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:message-id:subject:cc:to:from:date:from:to :cc:subject:date:message-id:reply-to; bh=1mthWbvJo8IBMcus1WaUcDoRStgqS1tIgAGD3vbm8lg=; b=iK/QAaUWyE51ITaxDsZRqBs8saimtkvfYnXoy33HzFrRI0O2t3xMkkLfFSNomVgqKf 4lI9avgV46WZ+9pngLLtyuS4TqfQU+Xb8GNHQIMYuLFU1AONqneCfAemzlMHt0L2b3YG BHLdRM4aN9CY92OsOnCQ0/uaffCdafmFoV3sINboOuU2SKieN3AuYZeaDZHv8E92L2O1 hmnoIOUX6jA14MKWDfJXJjB5qcv3EWNUyzWzRhaO+Q2mSEelNF8PoWpdJdyAon1tSY9U 8M3HUk4DbDlADCHLaFu0bT3o5CzN2I9b6WnL3hqIKcyAcu4RY9V5pFJlNoYWRzULuLXO Hukw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776444703; x=1777049503; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=1mthWbvJo8IBMcus1WaUcDoRStgqS1tIgAGD3vbm8lg=; b=dEd2p1cTj/h/kU3AzIFNJrGk0unrUToEeCGOkde9HR+LvxYmWXSk3pm9qgFLz52CK5 GWaO1hbp7rQC9ZL3DCmmSer7BFpFnLct4Udxm9/Y+lEcTPJ5poDp0A/+9AjaI1YyS7Vr 53RV/jLsNtFkWXP9/Iu5QlsdO8G3p83Ch/49fn+7IeF1K+T3B/u0grBCHPkX/tXACnuz RMCNVv+Cs/zOrSaHxDkNJF5Kw7KgOrGQyamGHErNIdb7vbO2LoULbvC2v8xtqpyuZTcV N0xdCjGdOKe7i349wpWC8okqnD59MPN9X5keued77y6dbnDP3/r5WQL4Txf/q8hmFYkA jHWw== X-Forwarded-Encrypted: i=1; AFNElJ8Q6Xqjxpy7CDoWJ4DI1AhW7HWdfU8YHLbdYMnuA95BL6ZEqDCnIcZJZHaP589HphD3pwxAcGv5MIjTDxY=@vger.kernel.org X-Gm-Message-State: AOJu0YwJLQq6iTqpsDNhSrtlRhejYcxvFC3iOANlxNcWy+BrATyRwJlM obI/VRdbsy1i7MPp7pJW86rIcfHPDvMa2vy64vQHmNI67hLjmNMJVa/o X-Gm-Gg: AeBDievI8suzLVSbis2NF17vwry1LrC1FRt9TpjwPQY5EjO7eXhw42OZaQ/eacFTKrq Y3utkbLRckIdyKs1K+BPFLvCSxzK/vge6EwJW8m5kvs1WHTqBO0RW1+DuZ3p3pIifjr5rNZBK8W HA3JC+vOfUO/PfWfUuFJ6iz5yE7jW36VS3/KvEj4kKPUuBqKjW+CgAQakfeArOTGfbOtbzYuxRW 93NMmcovtaI9GY6FcCKVNXGSV/ydEVanWYMKy/bukFsRig/Mhs5f/LY0IGZwy5B+q1HM2FLQ/tm 2zkOKGh0XOc7O3l0LBSa2cNMHwxtBlbFKAipGXkU4f6EgCY3jnlMouWQFyMhcW5V5D0eriWUVo0 YqzIzM6rEbJlwLGuFaG6+8MRsHPpkzRG3e7VItF3nDakIuROr0YN0e2clktNnYc4PbkPGcwPJ/h O//XvBGfa+TSZeAn+wYDnofOJev3Oz/KTQnnAMbKOHs0p/Uunq3I1c3tKfSVdKySmQYRbP X-Received: by 2002:a05:690c:9990:b0:7b9:f136:56d8 with SMTP id 00721157ae682-7b9f1365e67mr19384947b3.30.1776444703340; Fri, 17 Apr 2026 09:51:43 -0700 (PDT) Received: from suesslenovo ([129.222.87.6]) by smtp.gmail.com with ESMTPSA id 00721157ae682-7b9ee99bb32sm8723277b3.30.2026.04.17.09.51.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Apr 2026 09:51:42 -0700 (PDT) Date: Fri, 17 Apr 2026 12:51:40 -0400 From: Justin Suess To: =?iso-8859-1?Q?Micka=EBl_Sala=FCn?= Cc: Song Liu , ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org, gnoack@google.com, jack@suse.cz, jmorris@namei.org, serge@hallyn.com, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, eddyz87@gmail.com, john.fastabend@gmail.com, sdf@fomichev.me, skhan@linuxfoundation.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: Re: [RFC PATCH 08/20] bpf: Add Landlock ruleset map type Message-ID: References: <20260407200157.3874806-1-utilityemal77@gmail.com> <20260407200157.3874806-9-utilityemal77@gmail.com> <20260417.ohgoh0Eecome@digikod.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20260417.ohgoh0Eecome@digikod.net> On Fri, Apr 17, 2026 at 05:18:05PM +0200, Mickaël Salaün wrote: > On Fri, Apr 17, 2026 at 10:09:13AM -0400, Justin Suess wrote: > > On Thu, Apr 16, 2026 at 04:47:40PM -0700, Song Liu wrote: > > > On Thu, Apr 16, 2026 at 2:53 PM Justin Suess wrote: > > > [...] > > > > I don't think we can pass the FD number via a map, since the FD is > > > > process specific. And it needs to be done in a way where we can lookup > > > > the specific ruleset the FD points to safely. > > > > > > > > So we'd need some other way to load the ruleset from a file descriptor, > > > > either through a new userspace side BPF call or similar mechanism. > > > > > > > > Is there some other common pattern for FDs --> kptr I can follow? > > > > > > I didn't find an exact example like this. There must be a way to achieve > > > this. In the worst case, we can add a kfunc for this. > > > > > > > I think new kfunc is a doable approach. I could make a kfunc taking a struct > > *task_struct and an FD that looks up a landlock ruleset within a given > > task that returns a trusted kptr. > > > > Something like: > > > > struct bpf_landlock_ruleset* bpf_landlock_get_ruleset_from_fd(struct > > task_struct* task, int fd) Thanks Mickaël and Song, There are definitely pros and cons to both approaches. I think it would be OK to have a dedicated map and indeed cleaner from the userspace side since there would be no intermediate step to find the task and lookup the fd since that would be handled by the map_ops. Cons of the new map type: The main issue is with the new map type is then we are limited to the specific data structure we define in the map. For instance if we want to use a hash or other data structure instead of an array to store rulesets, we'd need to define variants of the landlock map type for all data structures. So this kind of bungles the "data structure" and "data type" layers. Pros of the new map type: The ruleset_fd conversion would be implicitly handled by the map_ops. Userspace could insert the fd and bpf would not have to deal with it at all. Cons of bpf_landlock_get_ruleset_from_fd: Awkward conversion step. We need to find the task of the original ruleset creator and recieve the fd before looking it up and converting it to a kptr to the bpf_landlock_ruleset. Pros of bpf_landlock_get_ruleset_from_fd: We can use any existing map data structure to store our kptrs. Not having a dedicated map type simplifies implementation. ... Appreciate the feedback from both of you. > > That looks like a hack that would not handle FD's (object) lifetime > (e.g. what happen when the task is gone?). > If we take an underlying reference on the ruleset backing the fd, then the fd being closed shouldn't matter right? This is how the lifetime management works for that bpf_landlock_get_ruleset_from fd, in my draft implementation prior to the RFC: /** * bpf_landlock_get_ruleset_from_fd - acquire a Landlock ruleset from a task FD * @task: task owning the file descriptor table to look up * @fd: Landlock ruleset file descriptor in @task * * Returns: a referenced opaque Landlock ruleset, or NULL if the FD lookup or * validation fails. */ __bpf_kfunc struct bpf_landlock_ruleset * bpf_landlock_get_ruleset_from_fd(struct task_struct *task, int fd) { struct landlock_ruleset *ruleset; /* does landlock_get_ruleset and increments refcount */ ruleset = landlock_get_task_ruleset_from_fd(task, fd, FMODE_CAN_READ); if (IS_ERR(ruleset)) return NULL; return (struct bpf_landlock_ruleset *)ruleset; } The landlock_get_task_ruleset_from_fd increments the usage with landlock_get_ruleset. (This may sleep, so it must be tagged with KF_SLEEPABLE) If the fd is closed before landlock_get_ruleset_from_fd, then null is returned. The verifier will force the program to do the null check b/c of KF_RET_NULL. __kptr also have destructor kfuncs. So if we get the reference in bpf_landlock_get_ruleset_from_fd with landlock_get_ruleset_from_fd, and put the reference to the ruleset. The destructor path looks like this: /* Define ID for destructor * / BTF_ID_LIST(bpf_landlock_dtor_ids) BTF_ID(struct, bpf_landlock_ruleset) BTF_ID(func, bpf_landlock_put_ruleset_dtor) ... /** * bpf_landlock_put_ruleset - put a Landlock ruleset * @ruleset: Landlock ruleset to put */ __bpf_kfunc void bpf_landlock_put_ruleset(const struct bpf_landlock_ruleset *ruleset) { landlock_put_ruleset((struct landlock_ruleset *)ruleset); } __bpf_kfunc void bpf_landlock_put_ruleset_dtor(void *ruleset) { bpf_landlock_put_ruleset(ruleset); } static int __init bpf_landlock_kfunc_init(void) { const struct btf_id_dtor_kfunc bpf_landlock_dtors[] = { { .btf_id = bpf_landlock_dtor_ids[0], .kfunc_btf_id = bpf_landlock_dtor_ids[1], }, }; int ret; ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_landlock_kfunc_set); if (ret) return ret; return register_btf_id_dtor_kfuncs(bpf_landlock_dtors, ARRAY_SIZE(bpf_landlock_dtors), THIS_MODULE); } Good reminder I need to include a test making sure the ruleset remains valid after the FD and/or task is closed. :) > Why not using proper typing with a dedicated map? > I may be misunderstanding, but from what I see, a __kptr DOES give proper typing, __kptr is an annotation not a type. This is what it would look like in an BPF_MAP_TYPE_ARRAY. struct ruleset_kptr_value { struct bpf_landlock_ruleset __kptr * ruleset; }; struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, struct ruleset_kptr_value); } ruleset_kptr_map SEC(".maps"); So we get proper typing from what I see. (It's not like a __kptr is a special void*, it has a type) > > > > And tagging it with KF_ACQUIRE + KF_RET_NULL. > > > > Then keep the existing kfunc for putting the ruleset and enforcing it on > > a struct linux_binprm. > > > > The BPF program would need to get a reference to a task struct > > of the program creating the rulesets with bpf_task_from_pid for > > instance. Then they could use the task_struct with another plain integer > > map to store FD numbers and then use the rulesets or store them in a map > > of __kptr objects for later usage. > > > > Would this be more acceptable? > > > > Basically the pattern I need is userspace must create the file > > > > descriptor, BPF converts that FD into a refcounted kernel object, and > > > > even if userspace closes the FD BPF needs to hold a reference on the > > > > underlying ruleset structure. > > > > > > > > (In this patch this was accomplished through the map_ops) > > > > > > > > Let me know what you think Song. I do understand the benefit of having a > > > > __kptr instead, the refcounting is all there, and it would allow storing > > > > rulesets in multiple map types. (and one less map type to maintain). > > > > > > A new type of map for each FD referenced kernel type is non-starter. > > > It is impossible to add UAPI for a specific use case. > > This new map type is only about one file descriptor type, similarly to > socket FDs. From a UAPI point of view, it looks clean and safe, > especially to deal with underlying object lifetime (e.g. reference > tracking). > > > > > > You've convinced me. I could see a lot of problems if everyone wanting > > to add their specialized maps, it would be difficult to maintain. > > Is there another way to properly handle kernel object lifetime (not tied The answer the the lifetime part is yes. The kptr destructors and the landlock ruleset refcounting give us that abstraction. (along with the KF_ACQUIRE/KF_RELEASE annotations and destructor implementation) > to the caller) and pass them as file descriptor? This "pass them as a file descriptor" is the tricky part. It would be very convenient if we could send the fd to bpf from userspace and have it be implicitly converted (like in the BPF_MAP_TYPE_LANDLOCK_RULESET implementation) in one step, but I just don't see a way to do that with the bpf_landlock_get_ruleset_from_fd kfunc approach. > > > > > It's probably best to keep the specialized map types to core kernel > > interfaces only that are unlikely to change. > > File descriptors are a stable interface. > No that's correct. I cede that point :) > > > > > Thanks, > > > Song > > > > > > > Mickaël, do you have any thoughts on this? I have v2 basically ready, > > > > although it uses the BPF_MAP_TYPE_LANDLOCK_RULESET it changes a lot on > > > > the Landlock side. > >