diff mbox series

[bpf-next,2/5] bpf, fsverity: Add kfunc bpf_get_fsverity_digest

Message ID 20231013182644.2346458-3-song@kernel.org (mailing list archive)
State Superseded
Headers show
Series bpf: file verification with LSM and fsverity | expand

Commit Message

Song Liu Oct. 13, 2023, 6:26 p.m. UTC
The kfunc can be used to read fsverity_digest, so that we can verify
signature in BPF LSM.

This kfunc is added to fs/verity/measure.c because some data structure used
in the function is private to fsverity (fs/verity/fsverity_private.h).

Signed-off-by: Song Liu <song@kernel.org>
---
 fs/verity/measure.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

Comments

Eric Biggers Oct. 15, 2023, 7:07 a.m. UTC | #1
On Fri, Oct 13, 2023 at 11:26:41AM -0700, Song Liu wrote:
> The kfunc can be used to read fsverity_digest, so that we can verify
> signature in BPF LSM.
> 
> This kfunc is added to fs/verity/measure.c because some data structure used
> in the function is private to fsverity (fs/verity/fsverity_private.h).
> 
> Signed-off-by: Song Liu <song@kernel.org>
> ---
>  fs/verity/measure.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 66 insertions(+)
> 
> diff --git a/fs/verity/measure.c b/fs/verity/measure.c
> index eec5956141da..2d4b2e6f5a5d 100644
> --- a/fs/verity/measure.c
> +++ b/fs/verity/measure.c
> @@ -8,6 +8,8 @@
>  #include "fsverity_private.h"
>  
>  #include <linux/uaccess.h>
> +#include <linux/bpf.h>
> +#include <linux/btf.h>
>  
>  /**
>   * fsverity_ioctl_measure() - get a verity file's digest
> @@ -100,3 +102,67 @@ int fsverity_get_digest(struct inode *inode,
>  	return hash_alg->digest_size;
>  }
>  EXPORT_SYMBOL_GPL(fsverity_get_digest);
> +
> +/* bpf kfuncs */
> +__diag_push();
> +__diag_ignore_all("-Wmissing-prototypes",
> +		  "kfuncs which will be used in BPF programs");
> +
> +/**
> + * bpf_get_fsverity_digest: read fsverity digest of file
> + * @file: file to get digest from
> + * @digest_ptr: (out) dynptr for struct fsverity_digest
> + *
> + * Read fsverity_digest of *file* into *digest_ptr*.
> + *
> + * Return: 0 on success, a negative value on error.
> + */
> +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
> +{
> +	const struct inode *inode = file_inode(file);
> +	struct fsverity_digest *arg = digest_ptr->data;

What alignment is guaranteed here?

> +	const struct fsverity_info *vi;
> +	const struct fsverity_hash_alg *hash_alg;
> +	int out_digest_sz;
> +
> +	if (__bpf_dynptr_size(digest_ptr) < sizeof(struct fsverity_digest))
> +		return -EINVAL;
> +
> +	vi = fsverity_get_info(inode);
> +	if (!vi)
> +		return -ENODATA; /* not a verity file */
> +
> +	hash_alg = vi->tree_params.hash_alg;
> +
> +	arg->digest_algorithm = hash_alg - fsverity_hash_algs;
> +	arg->digest_size = hash_alg->digest_size;
> +
> +	out_digest_sz = __bpf_dynptr_size(digest_ptr) - sizeof(struct fsverity_digest);
> +
> +	/* copy digest */
> +	memcpy(arg->digest, vi->file_digest,  min_t(int, hash_alg->digest_size, out_digest_sz));
> +
> +	/* fill the extra buffer with zeros */
> +	memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);

Can't 'out_digest_sz - hash_alg->digest_size' underflow?

> +
> +	return 0;
> +}
> +
> +__diag_pop();
> +
> +BTF_SET8_START(fsverity_set)
> +BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_SLEEPABLE)

Should it be sleepable?  Nothing in it sleeps, as far as I can tell.

> +BTF_SET8_END(fsverity_set)
> +
> +const struct btf_kfunc_id_set bpf_fsverity_set = {
> +	.owner = THIS_MODULE,
> +	.set = &fsverity_set,
> +};

static const?

> +
> +static int __init bpf_fsverity_init(void)
> +{
> +	return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
> +					 &bpf_fsverity_set);
> +}
> +
> +late_initcall(bpf_fsverity_init);

Maybe this should be called by the existing fsverity_init() initcall instead of
having a brand new initcall just for this.

Also, doesn't this all need to be guarded by a kconfig such as CONFIG_BPF?

Also, it looks like I'm being signed up to maintain this.  This isn't a stable
UAPI, right?  No need to document this in Documentation/?

- Eric
Song Liu Oct. 16, 2023, 8:10 p.m. UTC | #2
On Sun, Oct 15, 2023 at 12:07 AM Eric Biggers <ebiggers@kernel.org> wrote:
>
[...]
> > + */
> > +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
> > +{
> > +     const struct inode *inode = file_inode(file);
> > +     struct fsverity_digest *arg = digest_ptr->data;
>
> What alignment is guaranteed here?

drnptr doesn't not provide alignment guarantee for digest_ptr->data.
If we need alignment guarantee, we need to add it here.

>
> > +     const struct fsverity_info *vi;
> > +     const struct fsverity_hash_alg *hash_alg;
> > +     int out_digest_sz;
> > +
> > +     if (__bpf_dynptr_size(digest_ptr) < sizeof(struct fsverity_digest))
> > +             return -EINVAL;
> > +
> > +     vi = fsverity_get_info(inode);
> > +     if (!vi)
> > +             return -ENODATA; /* not a verity file */
> > +
> > +     hash_alg = vi->tree_params.hash_alg;
> > +
> > +     arg->digest_algorithm = hash_alg - fsverity_hash_algs;
> > +     arg->digest_size = hash_alg->digest_size;
> > +
> > +     out_digest_sz = __bpf_dynptr_size(digest_ptr) - sizeof(struct fsverity_digest);
> > +
> > +     /* copy digest */
> > +     memcpy(arg->digest, vi->file_digest,  min_t(int, hash_alg->digest_size, out_digest_sz));
> > +
> > +     /* fill the extra buffer with zeros */
> > +     memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);
>
> Can't 'out_digest_sz - hash_alg->digest_size' underflow?

Will fix this in the next version.

>
> > +
> > +     return 0;
> > +}
> > +
> > +__diag_pop();
> > +
> > +BTF_SET8_START(fsverity_set)
> > +BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_SLEEPABLE)
>
> Should it be sleepable?  Nothing in it sleeps, as far as I can tell.

Indeed, we can remove sleepable requirement here.

>
> > +BTF_SET8_END(fsverity_set)
> > +
> > +const struct btf_kfunc_id_set bpf_fsverity_set = {
> > +     .owner = THIS_MODULE,
> > +     .set = &fsverity_set,
> > +};
>
> static const?

Will fix in v2.

>
> > +
> > +static int __init bpf_fsverity_init(void)
> > +{
> > +     return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
> > +                                      &bpf_fsverity_set);
> > +}
> > +
> > +late_initcall(bpf_fsverity_init);
>
> Maybe this should be called by the existing fsverity_init() initcall instead of
> having a brand new initcall just for this.

Yeah, that would also work.

>
> Also, doesn't this all need to be guarded by a kconfig such as CONFIG_BPF?

Will add this in v2.

>
> Also, it looks like I'm being signed up to maintain this.  This isn't a stable
> UAPI, right?  No need to document this in Documentation/?

BPF kfuncs are not UAPI. They are as stable as exported symbols.
We do have some documents for BPF kfuncs, for example in
Documentation/bpf/cpumasks.rst.

Do you have a recommendation or preference on where we should
document this? AFAICT, we can either add it to fsverity.rst or somewhere
in Documentation/bpf/.

Thanks,
Song
Eric Biggers Oct. 17, 2023, 3:12 a.m. UTC | #3
On Mon, Oct 16, 2023 at 01:10:40PM -0700, Song Liu wrote:
> On Sun, Oct 15, 2023 at 12:07 AM Eric Biggers <ebiggers@kernel.org> wrote:
> >
> [...]
> > > + */
> > > +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
> > > +{
> > > +     const struct inode *inode = file_inode(file);
> > > +     struct fsverity_digest *arg = digest_ptr->data;
> >
> > What alignment is guaranteed here?
> 
> drnptr doesn't not provide alignment guarantee for digest_ptr->data.
> If we need alignment guarantee, we need to add it here.

So technically it's wrong to cast it to struct fsverity_digest, then.

> >
> > Also, it looks like I'm being signed up to maintain this.  This isn't a stable
> > UAPI, right?  No need to document this in Documentation/?
> 
> BPF kfuncs are not UAPI. They are as stable as exported symbols.
> We do have some documents for BPF kfuncs, for example in
> Documentation/bpf/cpumasks.rst.
> 
> Do you have a recommendation or preference on where we should
> document this? AFAICT, we can either add it to fsverity.rst or somewhere
> in Documentation/bpf/.

The BPF documentation seems like the right place.

- Eric
Song Liu Oct. 17, 2023, 5:35 a.m. UTC | #4
On Mon, Oct 16, 2023 at 8:12 PM Eric Biggers <ebiggers@kernel.org> wrote:
>
> On Mon, Oct 16, 2023 at 01:10:40PM -0700, Song Liu wrote:
> > On Sun, Oct 15, 2023 at 12:07 AM Eric Biggers <ebiggers@kernel.org> wrote:
> > >
> > [...]
> > > > + */
> > > > +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
> > > > +{
> > > > +     const struct inode *inode = file_inode(file);
> > > > +     struct fsverity_digest *arg = digest_ptr->data;
> > >
> > > What alignment is guaranteed here?
> >
> > drnptr doesn't not provide alignment guarantee for digest_ptr->data.
> > If we need alignment guarantee, we need to add it here.
>
> So technically it's wrong to cast it to struct fsverity_digest, then.

We can enforce alignment here. Would __aligned(2) be sufficient?

Thanks,
Song
Eric Biggers Oct. 17, 2023, 5:46 a.m. UTC | #5
On Mon, Oct 16, 2023 at 10:35:16PM -0700, Song Liu wrote:
> On Mon, Oct 16, 2023 at 8:12 PM Eric Biggers <ebiggers@kernel.org> wrote:
> >
> > On Mon, Oct 16, 2023 at 01:10:40PM -0700, Song Liu wrote:
> > > On Sun, Oct 15, 2023 at 12:07 AM Eric Biggers <ebiggers@kernel.org> wrote:
> > > >
> > > [...]
> > > > > + */
> > > > > +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
> > > > > +{
> > > > > +     const struct inode *inode = file_inode(file);
> > > > > +     struct fsverity_digest *arg = digest_ptr->data;
> > > >
> > > > What alignment is guaranteed here?
> > >
> > > drnptr doesn't not provide alignment guarantee for digest_ptr->data.
> > > If we need alignment guarantee, we need to add it here.
> >
> > So technically it's wrong to cast it to struct fsverity_digest, then.
> 
> We can enforce alignment here. Would __aligned(2) be sufficient?
> 

Do you mean something like the following:

	if (!IS_ALIGNED((uintptr_t)digest_ptr->data, __alignof__(*arg)))
		return -EINVAL;
Song Liu Oct. 17, 2023, 2:16 p.m. UTC | #6
> On Oct 16, 2023, at 10:46 PM, Eric Biggers <ebiggers@kernel.org> wrote:
> 
> On Mon, Oct 16, 2023 at 10:35:16PM -0700, Song Liu wrote:
>> On Mon, Oct 16, 2023 at 8:12 PM Eric Biggers <ebiggers@kernel.org> wrote:
>>> 
>>> On Mon, Oct 16, 2023 at 01:10:40PM -0700, Song Liu wrote:
>>>> On Sun, Oct 15, 2023 at 12:07 AM Eric Biggers <ebiggers@kernel.org> wrote:
>>>>> 
>>>> [...]
>>>>>> + */
>>>>>> +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
>>>>>> +{
>>>>>> +     const struct inode *inode = file_inode(file);
>>>>>> +     struct fsverity_digest *arg = digest_ptr->data;
>>>>> 
>>>>> What alignment is guaranteed here?
>>>> 
>>>> drnptr doesn't not provide alignment guarantee for digest_ptr->data.
>>>> If we need alignment guarantee, we need to add it here.
>>> 
>>> So technically it's wrong to cast it to struct fsverity_digest, then.
>> 
>> We can enforce alignment here. Would __aligned(2) be sufficient?
>> 
> 
> Do you mean something like the following:
> 
> if (!IS_ALIGNED((uintptr_t)digest_ptr->data, __alignof__(*arg)))
> return -EINVAL;

I was thinking about hard-coding the alignment requirement. 
__alignof__ is much better. Thanks for the suggestion!

Song
Andrii Nakryiko Oct. 17, 2023, 7:50 p.m. UTC | #7
On Fri, Oct 13, 2023 at 11:29 AM Song Liu <song@kernel.org> wrote:
>
> The kfunc can be used to read fsverity_digest, so that we can verify
> signature in BPF LSM.
>
> This kfunc is added to fs/verity/measure.c because some data structure used
> in the function is private to fsverity (fs/verity/fsverity_private.h).
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
>  fs/verity/measure.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 66 insertions(+)
>
> diff --git a/fs/verity/measure.c b/fs/verity/measure.c
> index eec5956141da..2d4b2e6f5a5d 100644
> --- a/fs/verity/measure.c
> +++ b/fs/verity/measure.c
> @@ -8,6 +8,8 @@
>  #include "fsverity_private.h"
>
>  #include <linux/uaccess.h>
> +#include <linux/bpf.h>
> +#include <linux/btf.h>
>
>  /**
>   * fsverity_ioctl_measure() - get a verity file's digest
> @@ -100,3 +102,67 @@ int fsverity_get_digest(struct inode *inode,
>         return hash_alg->digest_size;
>  }
>  EXPORT_SYMBOL_GPL(fsverity_get_digest);
> +
> +/* bpf kfuncs */
> +__diag_push();
> +__diag_ignore_all("-Wmissing-prototypes",
> +                 "kfuncs which will be used in BPF programs");
> +
> +/**
> + * bpf_get_fsverity_digest: read fsverity digest of file
> + * @file: file to get digest from
> + * @digest_ptr: (out) dynptr for struct fsverity_digest
> + *
> + * Read fsverity_digest of *file* into *digest_ptr*.
> + *
> + * Return: 0 on success, a negative value on error.
> + */
> +__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
> +{
> +       const struct inode *inode = file_inode(file);
> +       struct fsverity_digest *arg = digest_ptr->data;

this can be null

I think we need some internal helpers that are similar to
bpf_dynptr_slice() that would handle invalid dynptr cases, as well as
abstract away potentially non-contiguous memory dynptr points to.
WDYT?

> +       const struct fsverity_info *vi;
> +       const struct fsverity_hash_alg *hash_alg;
> +       int out_digest_sz;
> +
> +       if (__bpf_dynptr_size(digest_ptr) < sizeof(struct fsverity_digest))
> +               return -EINVAL;
> +
> +       vi = fsverity_get_info(inode);
> +       if (!vi)
> +               return -ENODATA; /* not a verity file */
> +
> +       hash_alg = vi->tree_params.hash_alg;
> +
> +       arg->digest_algorithm = hash_alg - fsverity_hash_algs;
> +       arg->digest_size = hash_alg->digest_size;
> +
> +       out_digest_sz = __bpf_dynptr_size(digest_ptr) - sizeof(struct fsverity_digest);
> +
> +       /* copy digest */
> +       memcpy(arg->digest, vi->file_digest,  min_t(int, hash_alg->digest_size, out_digest_sz));
> +
> +       /* fill the extra buffer with zeros */
> +       memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);
> +
> +       return 0;
> +}
> +
> +__diag_pop();
> +
> +BTF_SET8_START(fsverity_set)
> +BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_SLEEPABLE)
> +BTF_SET8_END(fsverity_set)
> +
> +const struct btf_kfunc_id_set bpf_fsverity_set = {
> +       .owner = THIS_MODULE,
> +       .set = &fsverity_set,
> +};
> +
> +static int __init bpf_fsverity_init(void)
> +{
> +       return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
> +                                        &bpf_fsverity_set);
> +}
> +
> +late_initcall(bpf_fsverity_init);
> --
> 2.34.1
>
diff mbox series

Patch

diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index eec5956141da..2d4b2e6f5a5d 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -8,6 +8,8 @@ 
 #include "fsverity_private.h"
 
 #include <linux/uaccess.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
 
 /**
  * fsverity_ioctl_measure() - get a verity file's digest
@@ -100,3 +102,67 @@  int fsverity_get_digest(struct inode *inode,
 	return hash_alg->digest_size;
 }
 EXPORT_SYMBOL_GPL(fsverity_get_digest);
+
+/* bpf kfuncs */
+__diag_push();
+__diag_ignore_all("-Wmissing-prototypes",
+		  "kfuncs which will be used in BPF programs");
+
+/**
+ * bpf_get_fsverity_digest: read fsverity digest of file
+ * @file: file to get digest from
+ * @digest_ptr: (out) dynptr for struct fsverity_digest
+ *
+ * Read fsverity_digest of *file* into *digest_ptr*.
+ *
+ * Return: 0 on success, a negative value on error.
+ */
+__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
+{
+	const struct inode *inode = file_inode(file);
+	struct fsverity_digest *arg = digest_ptr->data;
+	const struct fsverity_info *vi;
+	const struct fsverity_hash_alg *hash_alg;
+	int out_digest_sz;
+
+	if (__bpf_dynptr_size(digest_ptr) < sizeof(struct fsverity_digest))
+		return -EINVAL;
+
+	vi = fsverity_get_info(inode);
+	if (!vi)
+		return -ENODATA; /* not a verity file */
+
+	hash_alg = vi->tree_params.hash_alg;
+
+	arg->digest_algorithm = hash_alg - fsverity_hash_algs;
+	arg->digest_size = hash_alg->digest_size;
+
+	out_digest_sz = __bpf_dynptr_size(digest_ptr) - sizeof(struct fsverity_digest);
+
+	/* copy digest */
+	memcpy(arg->digest, vi->file_digest,  min_t(int, hash_alg->digest_size, out_digest_sz));
+
+	/* fill the extra buffer with zeros */
+	memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);
+
+	return 0;
+}
+
+__diag_pop();
+
+BTF_SET8_START(fsverity_set)
+BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_SLEEPABLE)
+BTF_SET8_END(fsverity_set)
+
+const struct btf_kfunc_id_set bpf_fsverity_set = {
+	.owner = THIS_MODULE,
+	.set = &fsverity_set,
+};
+
+static int __init bpf_fsverity_init(void)
+{
+	return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
+					 &bpf_fsverity_set);
+}
+
+late_initcall(bpf_fsverity_init);