diff mbox series

[v2,2/3] Add fanotify_get_supported_init_flags() helper function

Message ID 20221020130843.15147-3-mdoucha@suse.cz
State Rejected
Headers show
Series Fix fanotify14 | expand

Commit Message

Martin Doucha Oct. 20, 2022, 1:08 p.m. UTC
Since FAN_ALL_INIT_FLAGS constant is deprecated, the kernel has added
new fanotify feature flags and there is no other way to check
for their support, we need to manually check which init flags needed
by our tests are available.

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1:
- Fixed check for FAN_REPORT_NAME
- Added longer patch description

Thanks for pointing out the flag dependency between FAN_REPORT_NAME and
FAN_REPORT_DIR_FID. I must have misread the documentation on that one.
Since this appears to be the only flag with a dependency for now, let's
keep the special handling simple. If the kernel adds more flags that are
invalid on their own, we should handle that using a table.

These flag support checks will be needed in multiple tests so it's better
to have one common function that'll do them in one call than to copy-paste
multiple setup steps from one test to another.

Though it'd be great if kernel itself would provide a syscall that returns
all supported fanotify init, mark or mask flags in one call.

 testcases/kernel/syscalls/fanotify/fanotify.h | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

Comments

Amir Goldstein Oct. 20, 2022, 3:36 p.m. UTC | #1
On Thu, Oct 20, 2022 at 4:08 PM Martin Doucha <mdoucha@suse.cz> wrote:
>
> Since FAN_ALL_INIT_FLAGS constant is deprecated, the kernel has added
> new fanotify feature flags and there is no other way to check
> for their support, we need to manually check which init flags needed
> by our tests are available.
>
> Signed-off-by: Martin Doucha <mdoucha@suse.cz>
> ---
>
> Changes since v1:
> - Fixed check for FAN_REPORT_NAME
> - Added longer patch description
>
> Thanks for pointing out the flag dependency between FAN_REPORT_NAME and
> FAN_REPORT_DIR_FID. I must have misread the documentation on that one.
> Since this appears to be the only flag with a dependency for now, let's
> keep the special handling simple. If the kernel adds more flags that are
> invalid on their own, we should handle that using a table.
>
> These flag support checks will be needed in multiple tests so it's better
> to have one common function that'll do them in one call than to copy-paste
> multiple setup steps from one test to another.
>
> Though it'd be great if kernel itself would provide a syscall that returns
> all supported fanotify init, mark or mask flags in one call.
>
>  testcases/kernel/syscalls/fanotify/fanotify.h | 26 +++++++++++++++++++
>  1 file changed, 26 insertions(+)
>
> diff --git a/testcases/kernel/syscalls/fanotify/fanotify.h b/testcases/kernel/syscalls/fanotify/fanotify.h
> index 51078103e..f3ac1630f 100644
> --- a/testcases/kernel/syscalls/fanotify/fanotify.h
> +++ b/testcases/kernel/syscalls/fanotify/fanotify.h
> @@ -213,6 +213,32 @@ static inline int fanotify_init_flags_supported_by_kernel(unsigned int flags)
>         return fanotify_init_flags_supported_on_fs(flags, NULL);
>  }
>
> +/*
> + * Check support of given init flags one by one and return those which are
> + * supported.
> + */
> +static inline unsigned int fanotify_get_supported_init_flags(unsigned int flags,
> +       const char *fname)
> +{
> +       unsigned int flg, arg, ret = 0;
> +
> +       for (flg = 1; flg; flg <<= 1) {
> +               if (!(flags & flg))
> +                       continue;
> +
> +               arg = flg;
> +
> +               // FAN_REPORT_NAME is invalid without FAN_REPORT_DIR_FID
> +               if (flg == FAN_REPORT_NAME)
> +                       arg |= FAN_REPORT_DIR_FID;
> +

NACK
this is not the only dependency
this is not a valid generic function.

I only gave a recipe in v1 review how I think the checks should be done.

Thanks,
Amir.
Martin Doucha Oct. 21, 2022, 1:49 p.m. UTC | #2
On 20. 10. 22 17:36, Amir Goldstein wrote:
> NACK
> this is not the only dependency
> this is not a valid generic function.
> 
> I only gave a recipe in v1 review how I think the checks should be done.

I still want to make something that is easy to reuse in other fanotify 
tests. It doesn't have to be fully generic. If you want, I can add a 
list of manually validated init flags into the support check function 
and make the function terminate the program when somebody passes flags 
that haven't been validated. That'll ensure that the flag dependency 
list will be kept up to date.
Amir Goldstein Oct. 21, 2022, 7:03 p.m. UTC | #3
On Fri, Oct 21, 2022 at 4:49 PM Martin Doucha <mdoucha@suse.cz> wrote:
>
> On 20. 10. 22 17:36, Amir Goldstein wrote:
> > NACK
> > this is not the only dependency
> > this is not a valid generic function.
> >
> > I only gave a recipe in v1 review how I think the checks should be done.
>
> I still want to make something that is easy to reuse in other fanotify
> tests. It doesn't have to be fully generic. If you want, I can add a
> list of manually validated init flags into the support check function
> and make the function terminate the program when somebody passes flags
> that haven't been validated. That'll ensure that the flag dependency
> list will be kept up to date.
>

I don't have a vision of what you are proposing.
Make a proposal and I will see if it is correct.

I must say I don't understand what it is that you are trying to improve.
All the test needs to know is if the specific combinations of flags that
the test uses are supported by the kernel/fs.

Trying to figure out which of the bits from a specific combination is
not supported? how does that help users?
Maybe in kernel 5.10 flag X is supported and in kernel 5.11 flag
Y is also supported, but only in kernel 5.12 the combination X | Y
is supported? Do you see why your generic function doesn't make
much sense? or is just too complex to be worth the trouble
for an informational print?

Thanks,
Amir.
Martin Doucha Oct. 24, 2022, 9:03 a.m. UTC | #4
On 21. 10. 22 21:03, Amir Goldstein wrote:
> I don't have a vision of what you are proposing.
> Make a proposal and I will see if it is correct.
> 
> I must say I don't understand what it is that you are trying to improve.
> All the test needs to know is if the specific combinations of flags that
> the test uses are supported by the kernel/fs.
> 
> Trying to figure out which of the bits from a specific combination is
> not supported? how does that help users?
> Maybe in kernel 5.10 flag X is supported and in kernel 5.11 flag
> Y is also supported, but only in kernel 5.12 the combination X | Y
> is supported? Do you see why your generic function doesn't make
> much sense? or is just too complex to be worth the trouble
> for an informational print?

The function I'm trying to write is supposed to check whether a 
particular flag is implemented by the kernel. Whether a particular 
combination of implemented flags is also *allowed* is out of scope.

Note that the test I'm writing this for is fanotify14, which checks that 
various invalid combinations of flags will correctly return error. But 
since the error code for "this flag is not implemented" and "this flag 
was used incorrectly" is the same, I need to somehow get the fanotify 
feature set so that I can skip test cases which are not compatible with 
the running kernel. I don't really care about which specific flag is not 
implemented, comparing flags against a bitmask is just a quick and 
convenient way to check that running the test case makes sense.
Amir Goldstein Oct. 24, 2022, 1:08 p.m. UTC | #5
On Mon, Oct 24, 2022, 12:03 PM Martin Doucha <mdoucha@suse.cz> wrote:

> On 21. 10. 22 21:03, Amir Goldstein wrote:
> > I don't have a vision of what you are proposing.
> > Make a proposal and I will see if it is correct.
> >
> > I must say I don't understand what it is that you are trying to improve.
> > All the test needs to know is if the specific combinations of flags that
> > the test uses are supported by the kernel/fs.
> >
> > Trying to figure out which of the bits from a specific combination is
> > not supported? how does that help users?
> > Maybe in kernel 5.10 flag X is supported and in kernel 5.11 flag
> > Y is also supported, but only in kernel 5.12 the combination X | Y
> > is supported? Do you see why your generic function doesn't make
> > much sense? or is just too complex to be worth the trouble
> > for an informational print?
>
> The function I'm trying to write is supposed to check whether a
> particular flag is implemented by the kernel. Whether a particular
> combination of implemented flags is also *allowed* is out of scope.
>
> Note that the test I'm writing this for is fanotify14, which checks that
> various invalid combinations of flags will correctly return error. But
> since the error code for "this flag is not implemented" and "this flag
> was used incorrectly" is the same, I need to somehow get the fanotify
> feature set so that I can skip test cases which are not compatible with
> the running kernel. I don't really care about which specific flag is not
> implemented, comparing flags against a bitmask is just a quick and
> convenient way to check that running the test case makes sense.
>


Why is skipping the test better than passing the test?

The test wants to know that a specific flag combination is not allowed.

It is particarly not allowed also on old kernels that do not support either
individual flag.

What's the difference?

Who is going to gain anything from this change?

Sorry for being strict on this point
I may be missing something.

Please clarify what it is the problem use case is and I will suggest a
solution, because I disagree with this solution.

Thanks,
Amir.


> --
> Martin Doucha   mdoucha@suse.cz
> QA Engineer for Software Maintenance
> SUSE LINUX, s.r.o.
> CORSO IIa
> Krizikova 148/34
> 186 00 Prague 8
> Czech Republic
>
>
Martin Doucha Oct. 24, 2022, 1:16 p.m. UTC | #6
On 24. 10. 22 15:08, Amir Goldstein wrote:
> Why is skipping the test better than passing the test?
> 
> The test wants to know that a specific flag combination is not allowed.
> 
> It is particarly not allowed also on old kernels that do not support 
> either individual flag.
> 
> What's the difference?
> 
> Who is going to gain anything from this change?
> 
> Sorry for being strict on this point
> I may be missing something.
> 
> Please clarify what it is the problem use case is and I will suggest a 
> solution, because I disagree with this solution.

We're running fanotify14 on kernel 5.3 and one of the subtests (test 
case #8) is using a flag that was added in kernel 5.9. But in this case, 
fanotify_init() is supposed to pass and then fanotify_mark() is supposed 
to fail because of mismatch between mark() and init() flags. I'm trying 
to fix this particular subtest failure caused by wrong feature check.
Amir Goldstein Oct. 24, 2022, 2:34 p.m. UTC | #7
On Mon, Oct 24, 2022 at 4:16 PM Martin Doucha <mdoucha@suse.cz> wrote:
>
> On 24. 10. 22 15:08, Amir Goldstein wrote:
> > Why is skipping the test better than passing the test?
> >
> > The test wants to know that a specific flag combination is not allowed.
> >
> > It is particarly not allowed also on old kernels that do not support
> > either individual flag.
> >
> > What's the difference?
> >
> > Who is going to gain anything from this change?
> >
> > Sorry for being strict on this point
> > I may be missing something.
> >
> > Please clarify what it is the problem use case is and I will suggest a
> > solution, because I disagree with this solution.
>
> We're running fanotify14 on kernel 5.3 and one of the subtests (test
> case #8) is using a flag that was added in kernel 5.9. But in this case,
> fanotify_init() is supposed to pass and then fanotify_mark() is supposed
> to fail because of mismatch between mark() and init() flags. I'm trying
> to fix this particular subtest failure caused by wrong feature check.
>

Got it. If you wrote this information somewhere else
I must have missed it - if you did not, please write explicitly
which test case is being fixed on which kernel version/config.

This is how I would fix the problem:

        if (!tc->mask) {
                /* tc->expected_errno refers to the expected result of
fanotify_init() */
                TST_EXP_FD_OR_FAIL(fanotify_fd =
fanotify_init(tc->init_flags, O_RDONLY),
                                   tc->expected_errno);
        } else {
                /* tc->expected_errno refers to the expected result of
fanotify_mark() */
                fanotify_fd = fanotify_init(tc->init_flags, O_RDONLY);
                if (fanotify_fd < 0) {
                        if (errno == EINVAL)
                                tst_res(TCONF, "fanotify_init flags
XXX not supported");
                        else
                                tst_res(TFAIL, "fanotify_init failed");
                }
        }

Give or take using more macros and your nicer flag prints.
There is no need for auto-detection of the unsupported kernel flags.

If the test case is expected to get to fanotify_mark() (i.e. non-zero tc->mask)
EINVAL from fanotify_init() always means "unsupported".

Thanks,
Amir.
Martin Doucha Oct. 24, 2022, 2:58 p.m. UTC | #8
On 24. 10. 22 16:34, Amir Goldstein wrote:
> This is how I would fix the problem:
> 
> <snip>
> 
> Give or take using more macros and your nicer flag prints.
> There is no need for auto-detection of the unsupported kernel flags.
> 
> If the test case is expected to get to fanotify_mark() (i.e. non-zero tc->mask)
> EINVAL from fanotify_init() always means "unsupported".

This would be a good approach if fanotify_init() returned distinct error 
code for "flag not implemented", like ENOTSUP. But when EINVAL is 
returned for both "not implemented" and "wrong use", this has a risk of 
hiding real bugs. That's why I'm trying to detect the actual set of 
flags implemented in the running kernel before running the real tests.

And since some flags may be backported to older kernels, generating 
feature sets based on kernel version is not a solution.
Petr Vorel Oct. 24, 2022, 4:15 p.m. UTC | #9
Hi all,

> On 24. 10. 22 16:34, Amir Goldstein wrote:
> > This is how I would fix the problem:

> > <snip>

> > Give or take using more macros and your nicer flag prints.
> > There is no need for auto-detection of the unsupported kernel flags.

> > If the test case is expected to get to fanotify_mark() (i.e. non-zero tc->mask)
> > EINVAL from fanotify_init() always means "unsupported".

> This would be a good approach if fanotify_init() returned distinct error
> code for "flag not implemented", like ENOTSUP. But when EINVAL is returned
> for both "not implemented" and "wrong use", this has a risk of hiding real
> bugs. That's why I'm trying to detect the actual set of flags implemented in
> the running kernel before running the real tests.
Indeed, that's quite surprising (not really, it was added in 2.6.36 and remember
Jan Kara's talk about dnotify/inotify/fanotify history). I wonder if it's
possible to fix (backward compatibility would suffer).

> And since some flags may be backported to older kernels, generating feature
> sets based on kernel version is not a solution.
I guess even some not-important fix was not backported. I guess features aren't
backported to the stable kernel maybe to enterprise kernels (SLES, RHEL), but
even there I'd guess there are related patches backported, not features. But
maybe I'm wrong. Jan and Amir?

Kind regards,
Petr
Amir Goldstein Oct. 24, 2022, 4:18 p.m. UTC | #10
On Mon, Oct 24, 2022 at 5:58 PM Martin Doucha <mdoucha@suse.cz> wrote:
>
> On 24. 10. 22 16:34, Amir Goldstein wrote:
> > This is how I would fix the problem:
> >
> > <snip>
> >
> > Give or take using more macros and your nicer flag prints.
> > There is no need for auto-detection of the unsupported kernel flags.
> >
> > If the test case is expected to get to fanotify_mark() (i.e. non-zero tc->mask)
> > EINVAL from fanotify_init() always means "unsupported".
>
> This would be a good approach if fanotify_init() returned distinct error
> code for "flag not implemented", like ENOTSUP. But when EINVAL is
> returned for both "not implemented" and "wrong use", this has a risk of
> hiding real bugs.

Show me how this could hide a real bug.
Give an example.
It does not need to be a specific kernel
use an example with imaginary kernel with a backported feature if you like.

fanotify14 is not about making sure that flag combinations are allowed
it is about making sure that flag combinations are not allowed.

If the test case is testing illegal init flags, the outcome must be
fanotify_init
EINVAL.

If the test case is testing illegal mark flags, the outcome of fanotify_init
may be EINVAL meaning that this test case will be skipped.
It does not matter which specific init flag or init flag combination
causes this EINVAL.

I am ready to be proven wrong, but with examples,
like the one you provided with test case #8 and kernel 5.3.
hand waving and talking about vague "real bugs" won't convince me.

Thanks,
Amir.
Amir Goldstein Oct. 24, 2022, 4:30 p.m. UTC | #11
On Mon, Oct 24, 2022 at 7:15 PM Petr Vorel <pvorel@suse.cz> wrote:
>
> Hi all,
>
> > On 24. 10. 22 16:34, Amir Goldstein wrote:
> > > This is how I would fix the problem:
>
> > > <snip>
>
> > > Give or take using more macros and your nicer flag prints.
> > > There is no need for auto-detection of the unsupported kernel flags.
>
> > > If the test case is expected to get to fanotify_mark() (i.e. non-zero tc->mask)
> > > EINVAL from fanotify_init() always means "unsupported".
>
> > This would be a good approach if fanotify_init() returned distinct error
> > code for "flag not implemented", like ENOTSUP. But when EINVAL is returned
> > for both "not implemented" and "wrong use", this has a risk of hiding real
> > bugs. That's why I'm trying to detect the actual set of flags implemented in
> > the running kernel before running the real tests.
> Indeed, that's quite surprising (not really, it was added in 2.6.36 and remember
> Jan Kara's talk about dnotify/inotify/fanotify history). I wonder if it's
> possible to fix (backward compatibility would suffer).
>

The kernel UAPI is not very consistent about ENOTSUP vs. EINVAL
renameat(2), unlink(2), link(2) and other return EINVAL for unsupported flags
fallocate(2), ioctl(2) and others return ENOTSUP for unsupported commands.
It's not a clear cut, but ENOTSUP is generally for unsupported fs methods,
not for unsupported options.

> > And since some flags may be backported to older kernels, generating feature
> > sets based on kernel version is not a solution.
> I guess even some not-important fix was not backported. I guess features aren't
> backported to the stable kernel maybe to enterprise kernels (SLES, RHEL), but
> even there I'd guess there are related patches backported, not features. But
> maybe I'm wrong. Jan and Amir?
>

I am fine with writing tests that are friendly to distros that want to backport
features, that is not what my complaint is about.
As I wrote to Martin, I will accept any imaginary kernel as an example
for why the test is wrong for that kernel, but I don't see the bug.

The desire to distinguish between "this kernel should support
these init flags but failed" and "this kernel does not support those init flags"
is not realistic, given that fanotify_init() return codes do not
distinguish between those two cases.

The suggested logic to work this out by testing flag by flags is faulty
because of current and future flag dependencies.

So show me a real bug, as Martin did, and we will fix it.

Thanks,
Amir.
Petr Vorel Oct. 24, 2022, 5:03 p.m. UTC | #12
> On Mon, Oct 24, 2022 at 7:15 PM Petr Vorel <pvorel@suse.cz> wrote:

> > Hi all,

> > > On 24. 10. 22 16:34, Amir Goldstein wrote:
> > > > This is how I would fix the problem:

> > > > <snip>

> > > > Give or take using more macros and your nicer flag prints.
> > > > There is no need for auto-detection of the unsupported kernel flags.

> > > > If the test case is expected to get to fanotify_mark() (i.e. non-zero tc->mask)
> > > > EINVAL from fanotify_init() always means "unsupported".

> > > This would be a good approach if fanotify_init() returned distinct error
> > > code for "flag not implemented", like ENOTSUP. But when EINVAL is returned
> > > for both "not implemented" and "wrong use", this has a risk of hiding real
> > > bugs. That's why I'm trying to detect the actual set of flags implemented in
> > > the running kernel before running the real tests.
> > Indeed, that's quite surprising (not really, it was added in 2.6.36 and remember
> > Jan Kara's talk about dnotify/inotify/fanotify history). I wonder if it's
> > possible to fix (backward compatibility would suffer).

Hi Amir,

> The kernel UAPI is not very consistent about ENOTSUP vs. EINVAL
> renameat(2), unlink(2), link(2) and other return EINVAL for unsupported flags
> fallocate(2), ioctl(2) and others return ENOTSUP for unsupported commands.
> It's not a clear cut, but ENOTSUP is generally for unsupported fs methods,
> not for unsupported options.

thanks for info, I didn't know that. Otherwise Martin's note to use ENOTSUP
would help.

> > > And since some flags may be backported to older kernels, generating feature
> > > sets based on kernel version is not a solution.
> > I guess even some not-important fix was not backported. I guess features aren't
> > backported to the stable kernel maybe to enterprise kernels (SLES, RHEL), but
> > even there I'd guess there are related patches backported, not features. But
> > maybe I'm wrong. Jan and Amir?


> I am fine with writing tests that are friendly to distros that want to backport
> features, that is not what my complaint is about.
> As I wrote to Martin, I will accept any imaginary kernel as an example
> for why the test is wrong for that kernel, but I don't see the bug.

> The desire to distinguish between "this kernel should support
> these init flags but failed" and "this kernel does not support those init flags"
> is not realistic, given that fanotify_init() return codes do not
> distinguish between those two cases.

> The suggested logic to work this out by testing flag by flags is faulty
> because of current and future flag dependencies.

> So show me a real bug, as Martin did, and we will fix it.
thanks for info. Fortunately there is no other bug, besides the one Martin
reported and is trying to fix.

Kind regards,
Petr

> Thanks,
> Amir.
Martin Doucha Oct. 25, 2022, 8:37 a.m. UTC | #13
On 24. 10. 22 19:03, Petr Vorel wrote:
>> The kernel UAPI is not very consistent about ENOTSUP vs. EINVAL
>> renameat(2), unlink(2), link(2) and other return EINVAL for unsupported flags
>> fallocate(2), ioctl(2) and others return ENOTSUP for unsupported commands.
>> It's not a clear cut, but ENOTSUP is generally for unsupported fs methods,
>> not for unsupported options.
> 
> thanks for info, I didn't know that. Otherwise Martin's note to use ENOTSUP
> would help.

I was not suggesting to change the kernel API, that's not a reasonable 
option at this point. I was just pointing out that the API design limits 
our options how to write reliable tests.
Martin Doucha Oct. 25, 2022, 8:51 a.m. UTC | #14
On 24. 10. 22 18:18, Amir Goldstein wrote:
> Show me how this could hide a real bug.
> Give an example.
> It does not need to be a specific kernel
> use an example with imaginary kernel with a backported feature if you like.
> 
> fanotify14 is not about making sure that flag combinations are allowed
> it is about making sure that flag combinations are not allowed.
> 
> If the test case is testing illegal init flags, the outcome must be
> fanotify_init
> EINVAL.
> 
> If the test case is testing illegal mark flags, the outcome of fanotify_init
> may be EINVAL meaning that this test case will be skipped.
> It does not matter which specific init flag or init flag combination
> causes this EINVAL.
> 
> I am ready to be proven wrong, but with examples,
> like the one you provided with test case #8 and kernel 5.3.
> hand waving and talking about vague "real bugs" won't convince me.

Imagine two init flags, A and B (doesn't matter which ones) that are not 
supposed to conflict in any way according to documentation. And we'll 
add 3 fanotify14 test cases with the following init calls:
- fanotify_init(A)
- fanotify_init(B)
- fanotify_init(A|B)

All 3 init calls are supposed to pass and then fanotify_mark() is 
supposed to fail. Now imagine that we have a buggy kernel where both 
flags are implemented but fanotify_init(A|B) hits a weird corner case 
and returns EINVAL. In your version of the code, the test will assume 
that it's due to a missing feature and report the test case as skipped. 
In my version of the code, the test will report a bug because it knows 
that all the required features are present.
Petr Vorel Oct. 25, 2022, 9:41 a.m. UTC | #15
> On 24. 10. 22 19:03, Petr Vorel wrote:
> > > The kernel UAPI is not very consistent about ENOTSUP vs. EINVAL
> > > renameat(2), unlink(2), link(2) and other return EINVAL for unsupported flags
> > > fallocate(2), ioctl(2) and others return ENOTSUP for unsupported commands.
> > > It's not a clear cut, but ENOTSUP is generally for unsupported fs methods,
> > > not for unsupported options.

> > thanks for info, I didn't know that. Otherwise Martin's note to use ENOTSUP
> > would help.

> I was not suggesting to change the kernel API, that's not a reasonable
> option at this point. I was just pointing out that the API design limits our
> options how to write reliable tests.

Sure, that was my suggestion. I meant it more as future improvement than to
solution to our problem, but I wasn't sure myself whether it be a good way.
It just remind me a different case, where errno was changed by accident and
kept that way (fix [1] was not accepted, instead the change was backported to
all live stable/LTS so that errno is at least consistent).

I also wonder whether real users of the API (not just test writers) would
appreciate to distinguish between these two cases. But anyway, I understand that
there would have to be a strong need for it to be reason to change, thus not
acceptable.

Kind regards,
Petr

[1] https://lore.kernel.org/netdev/20170630073448.GA9546@unicorn.suse.cz/
Jan Kara Oct. 25, 2022, 9:48 a.m. UTC | #16
On Tue 25-10-22 10:51:01, Martin Doucha wrote:
> On 24. 10. 22 18:18, Amir Goldstein wrote:
> > Show me how this could hide a real bug.
> > Give an example.
> > It does not need to be a specific kernel
> > use an example with imaginary kernel with a backported feature if you like.
> > 
> > fanotify14 is not about making sure that flag combinations are allowed
> > it is about making sure that flag combinations are not allowed.
> > 
> > If the test case is testing illegal init flags, the outcome must be
> > fanotify_init
> > EINVAL.
> > 
> > If the test case is testing illegal mark flags, the outcome of fanotify_init
> > may be EINVAL meaning that this test case will be skipped.
> > It does not matter which specific init flag or init flag combination
> > causes this EINVAL.
> > 
> > I am ready to be proven wrong, but with examples,
> > like the one you provided with test case #8 and kernel 5.3.
> > hand waving and talking about vague "real bugs" won't convince me.
> 
> Imagine two init flags, A and B (doesn't matter which ones) that are not
> supposed to conflict in any way according to documentation. And we'll add 3
> fanotify14 test cases with the following init calls:
> - fanotify_init(A)
> - fanotify_init(B)
> - fanotify_init(A|B)
> 
> All 3 init calls are supposed to pass and then fanotify_mark() is supposed
> to fail. Now imagine that we have a buggy kernel where both flags are
> implemented but fanotify_init(A|B) hits a weird corner case and returns
> EINVAL. In your version of the code, the test will assume that it's due to a
> missing feature and report the test case as skipped. In my version of the
> code, the test will report a bug because it knows that all the required
> features are present.

Yeah, but AFAIU you are trading expanded testing for possibility of false
test failures (because the situation that for some features A and B, both A
and B are implemented but A|B got implemented only later seems equally
probable scenario).

Since I don't find this critical to test (it seems like a relatively
unlikely mistake to do), I'd prefer less testing against false test
failures. If we want to be more precise, we should be spelling out in the
testcase (ideally using some common infrastructure) that if A & B is
supported, we also expect A|B (or even some C) to work.

								Honza
Amir Goldstein Oct. 25, 2022, 11:11 a.m. UTC | #17
On Tue, Oct 25, 2022 at 11:51 AM Martin Doucha <mdoucha@suse.cz> wrote:
>
> On 24. 10. 22 18:18, Amir Goldstein wrote:
> > Show me how this could hide a real bug.
> > Give an example.
> > It does not need to be a specific kernel
> > use an example with imaginary kernel with a backported feature if you like.
> >
> > fanotify14 is not about making sure that flag combinations are allowed
> > it is about making sure that flag combinations are not allowed.
> >
> > If the test case is testing illegal init flags, the outcome must be
> > fanotify_init
> > EINVAL.
> >
> > If the test case is testing illegal mark flags, the outcome of fanotify_init
> > may be EINVAL meaning that this test case will be skipped.
> > It does not matter which specific init flag or init flag combination
> > causes this EINVAL.
> >
> > I am ready to be proven wrong, but with examples,
> > like the one you provided with test case #8 and kernel 5.3.
> > hand waving and talking about vague "real bugs" won't convince me.
>
> Imagine two init flags, A and B (doesn't matter which ones) that are not
> supposed to conflict in any way according to documentation. And we'll
> add 3 fanotify14 test cases with the following init calls:
> - fanotify_init(A)
> - fanotify_init(B)
> - fanotify_init(A|B)
>
> All 3 init calls are supposed to pass and then fanotify_mark() is
> supposed to fail. Now imagine that we have a buggy kernel where both
> flags are implemented but fanotify_init(A|B) hits a weird corner case
> and returns EINVAL.
> In your version of the code, the test will assume
> that it's due to a missing feature and report the test case as skipped.
> In my version of the code, the test will report a bug because it knows
> that all the required features are present.
>

It is a valid test case to assert that the support for two flags is
independent,
but this is not the job of fanotify14.
fanotify14 checks for *illegal* flag combinations.

If you feel that there should be a test that verifies that
support of flag A is independent of support of flag B,
then please write a different test for that.

But then would you test all possible permutations of flags?
Not only flags that are used in fanotify14?
Not only flag pairs? but more concurrent flags?
I don't know if other APIs have such rigorous tests (except API fuzzers).

I agree with Jan that the value of such a test would be questionable,
but it does have a value, so I won't object to having this test, as
long as it does not blindly check for all the known fanotify init bits
are independent.

Asserting flag combination independence should be opt-in by the test
not out-out like you did with REPORT_FID and REPORT_NAME.

Thanks,
Amir.
Martin Doucha Oct. 25, 2022, 1:55 p.m. UTC | #18
On 25. 10. 22 11:48, Jan Kara wrote:
> On Tue 25-10-22 10:51:01, Martin Doucha wrote:
>> Imagine two init flags, A and B (doesn't matter which ones) that are not
>> supposed to conflict in any way according to documentation. And we'll add 3
>> fanotify14 test cases with the following init calls:
>> - fanotify_init(A)
>> - fanotify_init(B)
>> - fanotify_init(A|B)
>>
>> All 3 init calls are supposed to pass and then fanotify_mark() is supposed
>> to fail. Now imagine that we have a buggy kernel where both flags are
>> implemented but fanotify_init(A|B) hits a weird corner case and returns
>> EINVAL. In your version of the code, the test will assume that it's due to a
>> missing feature and report the test case as skipped. In my version of the
>> code, the test will report a bug because it knows that all the required
>> features are present.
> 
> Yeah, but AFAIU you are trading expanded testing for possibility of false
> test failures (because the situation that for some features A and B, both A
> and B are implemented but A|B got implemented only later seems equally
> probable scenario).
> 
> Since I don't find this critical to test (it seems like a relatively
> unlikely mistake to do), I'd prefer less testing against false test
> failures. If we want to be more precise, we should be spelling out in the
> testcase (ideally using some common infrastructure) that if A & B is
> supported, we also expect A|B (or even some C) to work.

This kind of failure may be highly unlikely on a vanilla kernel but it 
can easily happen due to incorrectly backported patches. IMHO it's 
better to get a failure and find out that the test case was invalid than 
to ignore a bug that may indicate deeper issues. We can always fix a 
broken test and possibly also update documentation of past changes in 
syscall behavior.

On 25. 10. 22 13:11, Amir Goldstein wrote:
> It is a valid test case to assert that the support for two flags is
> independent,
> but this is not the job of fanotify14.
> fanotify14 checks for *illegal* flag combinations.
> 
> If you feel that there should be a test that verifies that
> support of flag A is independent of support of flag B,
> then please write a different test for that.
> 
> But then would you test all possible permutations of flags?
> Not only flags that are used in fanotify14?
> Not only flag pairs? but more concurrent flags?
> I don't know if other APIs have such rigorous tests (except API fuzzers).
> 
> I agree with Jan that the value of such a test would be questionable,
> but it does have a value, so I won't object to having this test, as
> long as it does not blindly check for all the known fanotify init bits
> are independent.

We find a fair amount of kernel bugs not because we have a specific 
targeted testcase for them but because they break test setup for 
something else. The subtests in fanotify14 may not comprehensively test 
all combinations of init flags but it's still "free" test coverage that 
will be useful for catching bugs.

> Asserting flag combination independence should be opt-in by the test
> not out-out like you did with REPORT_FID and REPORT_NAME.

I don't understand this sentence, especially which patch it's referring to.
Amir Goldstein Oct. 25, 2022, 4:53 p.m. UTC | #19
On Tue, Oct 25, 2022 at 4:55 PM Martin Doucha <mdoucha@suse.cz> wrote:
>
> On 25. 10. 22 11:48, Jan Kara wrote:
> > On Tue 25-10-22 10:51:01, Martin Doucha wrote:
> >> Imagine two init flags, A and B (doesn't matter which ones) that are not
> >> supposed to conflict in any way according to documentation. And we'll add 3
> >> fanotify14 test cases with the following init calls:
> >> - fanotify_init(A)
> >> - fanotify_init(B)
> >> - fanotify_init(A|B)
> >>
> >> All 3 init calls are supposed to pass and then fanotify_mark() is supposed
> >> to fail. Now imagine that we have a buggy kernel where both flags are
> >> implemented but fanotify_init(A|B) hits a weird corner case and returns
> >> EINVAL. In your version of the code, the test will assume that it's due to a
> >> missing feature and report the test case as skipped. In my version of the
> >> code, the test will report a bug because it knows that all the required
> >> features are present.
> >
> > Yeah, but AFAIU you are trading expanded testing for possibility of false
> > test failures (because the situation that for some features A and B, both A
> > and B are implemented but A|B got implemented only later seems equally
> > probable scenario).
> >
> > Since I don't find this critical to test (it seems like a relatively
> > unlikely mistake to do), I'd prefer less testing against false test
> > failures. If we want to be more precise, we should be spelling out in the
> > testcase (ideally using some common infrastructure) that if A & B is
> > supported, we also expect A|B (or even some C) to work.
>
> This kind of failure may be highly unlikely on a vanilla kernel but it
> can easily happen due to incorrectly backported patches. IMHO it's
> better to get a failure and find out that the test case was invalid than
> to ignore a bug that may indicate deeper issues. We can always fix a
> broken test and possibly also update documentation of past changes in
> syscall behavior.
>
> On 25. 10. 22 13:11, Amir Goldstein wrote:
> > It is a valid test case to assert that the support for two flags is
> > independent,
> > but this is not the job of fanotify14.
> > fanotify14 checks for *illegal* flag combinations.
> >
> > If you feel that there should be a test that verifies that
> > support of flag A is independent of support of flag B,
> > then please write a different test for that.
> >
> > But then would you test all possible permutations of flags?
> > Not only flags that are used in fanotify14?
> > Not only flag pairs? but more concurrent flags?
> > I don't know if other APIs have such rigorous tests (except API fuzzers).
> >
> > I agree with Jan that the value of such a test would be questionable,
> > but it does have a value, so I won't object to having this test, as
> > long as it does not blindly check for all the known fanotify init bits
> > are independent.
>
> We find a fair amount of kernel bugs not because we have a specific
> targeted testcase for them but because they break test setup for
> something else. The subtests in fanotify14 may not comprehensively test
> all combinations of init flags but it's still "free" test coverage that
> will be useful for catching bugs.
>
> > Asserting flag combination independence should be opt-in by the test
> > not out-out like you did with REPORT_FID and REPORT_NAME.
>
> I don't understand this sentence, especially which patch it's referring to.
>

All right.

Let's see which flag combinations we have in the relevant tests in fanotify14:

FAN_REPORT_DFID_FID,
FAN_REPORT_DFID_NAME,
FAN_REPORT_DFID_NAME_TARGET,

That's all.

Support for FAN_REPORT_FID is a requirement for the entire test.

FAN_REPORT_TARGET_FID depends on all the rest of the flags
and support for it is already checked explicitly to skip test cases.

FAN_REPORT_NAME depends on FAN_REPORT_DIR_FID.

So effectively fanotify_get_supported_init_flags() only checks
FAN_REPORT_DIR_FID separately from the combination
FAN_REPORT_DFID_FID.

fanotify16, which tests *legal* combinations of these flags
already checks the separate flag FAN_REPORT_DIR_FID
so fanotify test cases with FAN_REPORT_DFID_FID would
fail if a kernel that supports FAN_REPORT_DIR_FID does not
support the combination FAN_REPORT_DFID_FID.

Bottom line:
fanotify_get_supported_init_flags() did not add any test coverage.

This is why it is a slippery slope to suggest fixes without
proving that there is a bug.

Thanks,
Amir.
Martin Doucha Oct. 26, 2022, 2:34 p.m. UTC | #20
On 25. 10. 22 18:53, Amir Goldstein wrote:
> All right.
> 
> Let's see which flag combinations we have in the relevant tests in fanotify14:
> 
> FAN_REPORT_DFID_FID,
> FAN_REPORT_DFID_NAME,
> FAN_REPORT_DFID_NAME_TARGET,
> 
> That's all.
> 
> Support for FAN_REPORT_FID is a requirement for the entire test.
> 
> FAN_REPORT_TARGET_FID depends on all the rest of the flags
> and support for it is already checked explicitly to skip test cases.
> 
> FAN_REPORT_NAME depends on FAN_REPORT_DIR_FID.
> 
> So effectively fanotify_get_supported_init_flags() only checks
> FAN_REPORT_DIR_FID separately from the combination
> FAN_REPORT_DFID_FID.
> 
> fanotify16, which tests *legal* combinations of these flags
> already checks the separate flag FAN_REPORT_DIR_FID
> so fanotify test cases with FAN_REPORT_DFID_FID would
> fail if a kernel that supports FAN_REPORT_DIR_FID does not
> support the combination FAN_REPORT_DFID_FID.
> 
> Bottom line:
> fanotify_get_supported_init_flags() did not add any test coverage.
> 
> This is why it is a slippery slope to suggest fixes without
> proving that there is a bug.

If this is a slippery slope, then we're already sliding down since 
commit fe31bfca which broke test case #8 because it's painfully hard to 
maintain multiple separate feature checks in setup() as you insist. I 
want to fix this mess with one simple helper function that will be 
reusable in all fanotify tests. The helper function is an up-to-date 
replacement for the old deprecated FAN_ALL_INIT_FLAGS macro. Nothing 
more, nothing less.
Petr Vorel May 10, 2023, 6:38 p.m. UTC | #21
Hi Amir, all,

> On Tue, Oct 25, 2022 at 4:55 PM Martin Doucha <mdoucha@suse.cz> wrote:

> > On 25. 10. 22 11:48, Jan Kara wrote:
> > > On Tue 25-10-22 10:51:01, Martin Doucha wrote:
> > >> Imagine two init flags, A and B (doesn't matter which ones) that are not
> > >> supposed to conflict in any way according to documentation. And we'll add 3
> > >> fanotify14 test cases with the following init calls:
> > >> - fanotify_init(A)
> > >> - fanotify_init(B)
> > >> - fanotify_init(A|B)

> > >> All 3 init calls are supposed to pass and then fanotify_mark() is supposed
> > >> to fail. Now imagine that we have a buggy kernel where both flags are
> > >> implemented but fanotify_init(A|B) hits a weird corner case and returns
> > >> EINVAL. In your version of the code, the test will assume that it's due to a
> > >> missing feature and report the test case as skipped. In my version of the
> > >> code, the test will report a bug because it knows that all the required
> > >> features are present.

> > > Yeah, but AFAIU you are trading expanded testing for possibility of false
> > > test failures (because the situation that for some features A and B, both A
> > > and B are implemented but A|B got implemented only later seems equally
> > > probable scenario).

> > > Since I don't find this critical to test (it seems like a relatively
> > > unlikely mistake to do), I'd prefer less testing against false test
> > > failures. If we want to be more precise, we should be spelling out in the
> > > testcase (ideally using some common infrastructure) that if A & B is
> > > supported, we also expect A|B (or even some C) to work.

> > This kind of failure may be highly unlikely on a vanilla kernel but it
> > can easily happen due to incorrectly backported patches. IMHO it's
> > better to get a failure and find out that the test case was invalid than
> > to ignore a bug that may indicate deeper issues. We can always fix a
> > broken test and possibly also update documentation of past changes in
> > syscall behavior.

> > On 25. 10. 22 13:11, Amir Goldstein wrote:
> > > It is a valid test case to assert that the support for two flags is
> > > independent,
> > > but this is not the job of fanotify14.
> > > fanotify14 checks for *illegal* flag combinations.

> > > If you feel that there should be a test that verifies that
> > > support of flag A is independent of support of flag B,
> > > then please write a different test for that.

> > > But then would you test all possible permutations of flags?
> > > Not only flags that are used in fanotify14?
> > > Not only flag pairs? but more concurrent flags?
> > > I don't know if other APIs have such rigorous tests (except API fuzzers).

> > > I agree with Jan that the value of such a test would be questionable,
> > > but it does have a value, so I won't object to having this test, as
> > > long as it does not blindly check for all the known fanotify init bits
> > > are independent.

> > We find a fair amount of kernel bugs not because we have a specific
> > targeted testcase for them but because they break test setup for
> > something else. The subtests in fanotify14 may not comprehensively test
> > all combinations of init flags but it's still "free" test coverage that
> > will be useful for catching bugs.

> > > Asserting flag combination independence should be opt-in by the test
> > > not out-out like you did with REPORT_FID and REPORT_NAME.

> > I don't understand this sentence, especially which patch it's referring to.


> All right.

> Let's see which flag combinations we have in the relevant tests in fanotify14:

> FAN_REPORT_DFID_FID,
> FAN_REPORT_DFID_NAME,
> FAN_REPORT_DFID_NAME_TARGET,

> That's all.

> Support for FAN_REPORT_FID is a requirement for the entire test.

> FAN_REPORT_TARGET_FID depends on all the rest of the flags
> and support for it is already checked explicitly to skip test cases.

> FAN_REPORT_NAME depends on FAN_REPORT_DIR_FID.

> So effectively fanotify_get_supported_init_flags() only checks
> FAN_REPORT_DIR_FID separately from the combination
> FAN_REPORT_DFID_FID.

> fanotify16, which tests *legal* combinations of these flags
> already checks the separate flag FAN_REPORT_DIR_FID
> so fanotify test cases with FAN_REPORT_DFID_FID would
> fail if a kernel that supports FAN_REPORT_DIR_FID does not
> support the combination FAN_REPORT_DFID_FID.

> Bottom line:
> fanotify_get_supported_init_flags() did not add any test coverage.

> This is why it is a slippery slope to suggest fixes without
> proving that there is a bug.

Naresh Kamboju reported [1]  5.4 mainline LTS kernels are failing on fanotify14,
exactly the same way Martin reported on SLES kernel based 5.3.18 (+ tons of
backported patches). Linaro did not mention this before, because they tested 5.4
on older LTP.

@Amir, Isn't this a reason to either accept these 2 Martin's patches or bring
another approach which fixes the detection in fanotify_init() ?

Kind regards,
Petr

[1] https://lore.kernel.org/ltp/CA+G9fYuT3N0LFaJGzQW2SYPJxEbEWLONDZO2OfBbeHNrsowy2w@mail.gmail.com/

> Thanks,
> Amir.
diff mbox series

Patch

diff --git a/testcases/kernel/syscalls/fanotify/fanotify.h b/testcases/kernel/syscalls/fanotify/fanotify.h
index 51078103e..f3ac1630f 100644
--- a/testcases/kernel/syscalls/fanotify/fanotify.h
+++ b/testcases/kernel/syscalls/fanotify/fanotify.h
@@ -213,6 +213,32 @@  static inline int fanotify_init_flags_supported_by_kernel(unsigned int flags)
 	return fanotify_init_flags_supported_on_fs(flags, NULL);
 }
 
+/*
+ * Check support of given init flags one by one and return those which are
+ * supported.
+ */
+static inline unsigned int fanotify_get_supported_init_flags(unsigned int flags,
+	const char *fname)
+{
+	unsigned int flg, arg, ret = 0;
+
+	for (flg = 1; flg; flg <<= 1) {
+		if (!(flags & flg))
+			continue;
+
+		arg = flg;
+
+		// FAN_REPORT_NAME is invalid without FAN_REPORT_DIR_FID
+		if (flg == FAN_REPORT_NAME)
+			arg |= FAN_REPORT_DIR_FID;
+
+		if (!fanotify_init_flags_supported_on_fs(arg, fname))
+			ret |= flg;
+	}
+
+	return ret;
+}
+
 typedef void (*tst_res_func_t)(const char *file, const int lineno,
 			       int ttype, const char *fmt, ...);