[LTP] [PATCH] syscalls/add_key05: add maxbytes/maxkeys test under unprivileged user

Yang Xu xuyang2018.jy@cn.fujitsu.com
Fri Feb 28 08:00:36 CET 2020


Hi!

> Hi!
> 
>> Hi!
>>> diff --git a/runtest/syscalls b/runtest/syscalls
>>> index 577a4a527..df7bbcbf1 100644
>>> -- 
>>> +void verify_max_btyes(void)
>>> +{
>>> +    char *buf, *invalid_buf;
>>> +    int plen, invalid_plen;
>>> +
>>> +    tst_res(TINFO, "test max bytes under unprivileged user");
>>> +    invalid_plen = max_bytes - used_bytes - data_len - 8;
>>
>> What is the -8 for, strlen("test_inv")?
> Yes.
>>
>> I guess that it will be more readable if we used the strlen() here as
>> well.
> OK. I will use strlen.
>>
>>> +    plen = invalid_plen - 1;
>>> +    buf = tst_alloc(plen);
>>> +    invalid_buf = tst_alloc(invalid_plen);
>>> +
>>> +    TEST(add_key("user", "test_inv", invalid_buf, invalid_plen, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +    if (TST_RET != -1)
>>> +        tst_res(TFAIL, "add_key(test_inv) succeeded unexpectedltly");
>>> +    else {
>>> +        if (TST_ERR == EDQUOT)
>>> +            tst_res(TPASS | TTERRNO, "add_key(test_inv) failed as 
>>> expected");
>>> +        else
>>> +            tst_res(TFAIL | TTERRNO, "add_key(test_inv) failed 
>>> expected EDQUOT got");
>>> +    }
>>> +
>>> +    TEST(add_key("user", "test_max", buf, plen, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +    if (TST_RET != -1) {
>>> +        tst_res(TPASS, "add_key(test_max) succeeded as expected");
>>> +        SAFE_FILE_LINES_SCANF("/proc/key-users", fmt, &used_key, 
>>> &max_key, &used_bytes, &max_bytes);
>>> +        if (used_bytes == max_bytes)
>>> +            tst_res(TPASS, "allow reaching the max bytes exactly");
>>> +        else
>>> +            tst_res(TFAIL, "max used bytes %u, key allow max bytes 
>>> %u", used_bytes, max_bytes);
>>> +    } else
>>> +        tst_res(TFAIL | TTERRNO, "add_key(test_max) failed 
>>> unexpectedly");
>>> +}
>>> +
>>> +void verify_max_keys(void)
>>> +{
>>> +    unsigned int i;
>>> +    char desc[10];
>>> +
>>> +    tst_res(TINFO, "test max keys under unprivileged user");
>>> +    for (i = used_key + 1; i < max_key; i++) {
>>> +        sprintf(desc, "abc%d", i);
>>> +        TEST(add_key("keyring", desc, keyring_buf, 0, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +        if (TST_RET == -1)
>>> +            tst_res(TFAIL | TTERRNO, "add keyring key(%s) failed", 
>>> desc);
>>
>> Why we are using the "keyring" ring here instead? I doubt that it
>> matters for the quota, but it just seems strange.
> It doesn't matter quota, only because "keyrings" plen is 0. I will use 
> "user" type for this.
>   >
>> Also we should stop the loop on a first failure, I guess that goto
>> count: would suffice.
> I ignored this before, thanks.
>>
>>> +    }
>>> +
>>> +    TEST(add_key("keyring", "abc200", keyring_buf, 0, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +
>>> +    if (TST_RET == -1) {
>>> +        tst_res(TFAIL | TTERRNO, "add keyring key(abc200) failed");
>>> +        goto count;
>>> +    } else
>>> +        tst_res(TPASS, "add keyring key(abc200) succedd");
>>
>> Why don't we just start the loop above at used_key? There is no point in
>> adding the last key here outside of the loop.
>>
> Using start + 1 more clean:
> abc100 >> the 100th key,
> We can change code (including last key)as below:
> for (i = used_key + 1; i <= max_key; i ++)
> 
>>> +    TEST(add_key("keyring", "abc201", keyring_buf, 0, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +    if (TST_RET != -1) {
>>> +        tst_res(TFAIL, "add keyring key(abc201) succeeded 
>>> unexpectedly");
>>> +        goto count;
>>
>> We should add a key with a different name than the previous "abc%d"
>> pattern here, if the upper limit was over 200 we would just replace a
>> key here instread which will not fail at all.
> Do you mean to use  "invalid_num_key" to avoid upper limit over 200?
> 
>>
>>> +    } else {
>>> +        if (TST_ERR == EDQUOT)
>>> +            tst_res(TPASS | TTERRNO, "add keyring key(abc201) failed 
>>> as expected over max key");
>>> +        else
>>> +            tst_res(TFAIL | TTERRNO, "add_keyring failed expected 
>>> EDQUOT got");
>>> +    }
>>> +count:
>>> +    SAFE_FILE_LINES_SCANF("/proc/key-users", fmt, &used_key, 
>>> &max_key, &used_bytes, &max_bytes);
>>> +    if (used_key == max_key)
>>> +        tst_res(TPASS, "allow reaching the max key exactly");
>>> +    else
>>> +        tst_res(TFAIL, "max used key %u, key allow max key %u", 
>>> used_key, max_key);
>>> +}
>>> +
>>> +static void do_test(unsigned int n)
>>> +{
>>> +    add_user();
>>> +    int f_used_bytes = 0;
>>> +
>>> +    if (!SAFE_FORK()) {
>>> +        SAFE_SETUID(ltpuser->pw_uid);
>>> +
>>> +        TEST(add_key("user", "test1", user_buf, 64, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +        if (TST_RET == -1)
>>> +            tst_brk(TFAIL | TTERRNO, "add key test1 failed");
>>> +        SAFE_FILE_LINES_SCANF("/proc/key-users", fmt, &used_key, 
>>> &max_key, &used_bytes, &max_bytes);
>>> +        f_used_bytes = used_bytes;
>>> +
>>> +        TEST(add_key("user", "test2", user_buf1, 64, 
>>> KEY_SPEC_THREAD_KEYRING));
>>> +        if (TST_RET == -1)
>>> +            tst_brk(TFAIL | TTERRNO, "add key test2 failed");
>>
>> Do we really need to pass a different users_buf to each call?
> I have seen kernel code, payload is used to instantiate or update the 
> key, I think it is no problme to use the same buf because we don't get 
> it again from kernel space.
>>
>>> +        SAFE_FILE_LINES_SCANF("/proc/key-users", fmt, &used_key, 
>>> &max_key, &used_bytes, &max_bytes);
>>> +
>>> +        data_len = used_bytes - f_used_bytes - strlen("test1") - 1 - 
>>> 64;
>>
>> So this code here is used to determine how much data will consume the
>> key structure in kernel itself? 

Sorry for the wrong info. This code is designed to compute delta, I have 
seen kernel code yestorday:
add_key calltrace as below:
add_key()
   key_create_or_update()
     key_alloc()
     __key_instantiate_and_link
       generic_key_instantiate
         key_payload_reserve
           ......

In key_alloc, we use type->def_datalen to alloc space, but when 
instantiating key, we using delta(quota_len -type->def_datalen) to 
adjust reserver space. So I compute this variable(delta, add_key05.c 
uses data_len) to avoid  that we can reach max bytes in key_alloc but 
got EDQOUT in key_payload_reserve.

more info:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/security/keys/core.rst#n1353

Also, using my v2 patch(send later), this case also fails on lastest 
kernel because of incomplete kernel commit.

more info:
https://patchwork.kernel.org/patch/11411507/

Best Regards
Yang Xu
> I debug this code after adding test1 key, show in /proc/keys
> add_key05.c:154: INFO: 1535d151 I--Q---    24 perm 3f030000     0     0 
> keyring   _ses: 1
> 
> add_key05.c:154: INFO: 15d7df09 I--Q---     6 perm 1f3f0000     0 65534 
> keyring   _uid.0: empty
> 
> add_key05.c:154: INFO: 1a300c72 I--Q---     1 perm 3f010000  1003     0 
> user      test1: 64
> 
> add_key05.c:154: INFO: 2e57738c I--Q---     1 perm 3f010000  1003     0 
> keyring   _tid: 1
> 
> the key num is 2 in /proc/key-users, I use this code to figure keyring 
>   _tid: 1(I don't know why has it) consumed data bytes.
> I guess that this is useless to run in
>> the case of testing for maximal number of keys, right? Can we put this
>> code into a function something as get_kernel_key_data_size() and call it
>> from the verify_max_bytes() only?
> Yes, it was only used in max_bytes.
>>
>> I guess that the SAFE_FILE_LINES_SCANF() intializes the max_key for the
>> max_keys test. Having a global variables that are initalized in random
>> places makes the code really confusing, can we avoid that if poissible
>> please?
> Ok. I will avoid this and make code clean.
>>
>> I guess that we can at least create:
>>
>> parse_proc_key_users(int *used_keys, int *max_key, int *used_bytes, 
>> *int max_bytes)
>>
>> function that would read the values into a local variables and copy the
>> results only if non-NULL pointers were passed.
>>
>> That way the verify_max_keys() would call:
>> parse_proc_key_users(NULL, &max_keys, NULL, NULL);
>> and use the result for the loop that adds the keys.
> Sound good, I will use it.
>>
>>> +        if (flag[n])
>>
>> Huh, why not just if (n)?
>>
> OK.
>>> +            verify_max_btyes();
>>> +        else
>>> +            verify_max_keys();
>>> +        exit(0);
>>> +    }
>>> +    tst_reap_children();
>>> +    clean_user();
>>
>> What is the reason to add and remove a user for each iteration?
>>
>> Aare you cleaning the keys that way?
> Yes, I planed to look for a wise (try KEYCTL_CLEAR
>   or KEYCTL_INVALIDATE)way but failed. Can you give me some
> suggestion?
>>
>>> +}
>>> +
>>> +static struct tst_test test = {
>>> +    .test = do_test,
>>> +    .tcnt = 2,
>>> +    .needs_root = 1,
>>> +    .forks_child = 1,
>>> +    .cleanup = clean_user,
>>> +    .bufs = (struct tst_buffers []) {
>>> +        {&keyring_buf, .size = 1},
>>> +        {&user_buf, .size = 64},
>>> +        {&user_buf1, .size = 64},
>>> +        {}
>>> +    },
>>> +    .tags = (const struct tst_tag[]) {
>>> +        {"linux-git", "a08bf91ce28"},
>>> +        {}
>>> +    }
>>> +};
>>> -- 
>>> 2.18.0
>>>
>>>
>>>
>>>
>>> -- 
>>> Mailing list info: https://lists.linux.it/listinfo/ltp
>>




More information about the ltp mailing list