C++:__aligned(__alignof__) 导致 char 数组数据出现问题?
C++: __aligned(__alignof__) causing problems with char array data?
Tl;dr: 我的代码正在访问的 char 数组以前指向预期的字符串值,现在在字符串之前有意外的字符。我目前的理论是,这在某种程度上是由 char 数组的对齐方式的变化引起的,我试图弄清楚我是否正确以及如何修复它。
我正在尝试维护一个用 C++ 编写的 Linux 内核模块。自从我使用 C++ 工作以来已经有很长一段时间了,所以我对我目前看到的东西有点困惑。
我们在内核模块之外有一个单独的实用程序,用于存储内核密钥环中的密钥。稍后内核模块会查找它并将其与预期的键值进行比较。这是查找方法:
#include <linux/key.h>
#include <keys/user-type.h>
int read_key(char *key_desc, char *expected_key) {
struct key *key;
struct user_key_payload *key_payload;
key = request_key(&key_type_user, key_desc, NULL);
key_payload = rcu_dereference(key->payload.data[0]);
return strncmp(key_payload->data, expected_key, key_payload->datalen));
}
此代码目前适用于我们支持的大多数内核版本。但是最近在一个特定的 OS 中进行了内核升级,现在我的 strncmp 调用找不到匹配项。数据似乎以某种方式偏移了。就像如果 key_payload->datalen = 16,并且 expected_key = "1234567890123456",那么 key_payload->data = "abcdef1234567890123456"。由于我正在比较前 16 个字符,因此显然不会匹配,但如果我从 key_payload->data[6] 开始,则会匹配。到目前为止 key_payload->datalen 仍然是正确的预期字符串长度。只是 key_payload->在新内核版本上更改的数据。
我查看了新内核中的几个补丁,我认为可能与该问题有关。我发现的一个明显区别是 this one。在补丁之前,user_key_payload 对象看起来像:
struct user_key_payload {
struct rcu_head rcu; /* RCU destructor */
unsigned short datalen; /* length of this data */
char data[0]; /* actual data */
};
但是之后看起来像:
struct user_key_payload {
struct rcu_head rcu; /* RCU destructor */
unsigned short datalen; /* length of this data */
char data[0] __aligned(__alignof__(u64)); /* actual data */
};
我不知道这是否是真正导致我出现问题的原因,但它似乎是一个显而易见的起点。我以前从未使用过 aligned 或 alignof 关键字。我读过一些关于对齐的文章,但它的水平比我多年来一直使用的要低得多。有谁知道 user_key_payload.data 的对齐方式是否会弄乱关键数据的预期内存地址,以及如何找出我的预期数据的实际位置?
char data[0]
表示结构之后的指针。它在内核中广泛用于指示 header 之后的 variable-length 有效负载。
__aligned(__alignof__(u64))
将数据对齐到 8 字节(64 位)边界。这意味着,例如,如果在 struct user_key_payload
中存储所有字段所需的大小是 10 个字节,那么结构将被填充以占用 16 个字节 (2*8)。
因此,如果内核威胁数据 8 字节对齐,但您的模块没有,那么 "abcdef" 就是您正在读取的填充。
编译模块时,始终确保包含用于编译内核的 header 文件的相同版本。
Tl;dr: 我的代码正在访问的 char 数组以前指向预期的字符串值,现在在字符串之前有意外的字符。我目前的理论是,这在某种程度上是由 char 数组的对齐方式的变化引起的,我试图弄清楚我是否正确以及如何修复它。
我正在尝试维护一个用 C++ 编写的 Linux 内核模块。自从我使用 C++ 工作以来已经有很长一段时间了,所以我对我目前看到的东西有点困惑。
我们在内核模块之外有一个单独的实用程序,用于存储内核密钥环中的密钥。稍后内核模块会查找它并将其与预期的键值进行比较。这是查找方法:
#include <linux/key.h>
#include <keys/user-type.h>
int read_key(char *key_desc, char *expected_key) {
struct key *key;
struct user_key_payload *key_payload;
key = request_key(&key_type_user, key_desc, NULL);
key_payload = rcu_dereference(key->payload.data[0]);
return strncmp(key_payload->data, expected_key, key_payload->datalen));
}
此代码目前适用于我们支持的大多数内核版本。但是最近在一个特定的 OS 中进行了内核升级,现在我的 strncmp 调用找不到匹配项。数据似乎以某种方式偏移了。就像如果 key_payload->datalen = 16,并且 expected_key = "1234567890123456",那么 key_payload->data = "abcdef1234567890123456"。由于我正在比较前 16 个字符,因此显然不会匹配,但如果我从 key_payload->data[6] 开始,则会匹配。到目前为止 key_payload->datalen 仍然是正确的预期字符串长度。只是 key_payload->在新内核版本上更改的数据。
我查看了新内核中的几个补丁,我认为可能与该问题有关。我发现的一个明显区别是 this one。在补丁之前,user_key_payload 对象看起来像:
struct user_key_payload {
struct rcu_head rcu; /* RCU destructor */
unsigned short datalen; /* length of this data */
char data[0]; /* actual data */
};
但是之后看起来像:
struct user_key_payload {
struct rcu_head rcu; /* RCU destructor */
unsigned short datalen; /* length of this data */
char data[0] __aligned(__alignof__(u64)); /* actual data */
};
我不知道这是否是真正导致我出现问题的原因,但它似乎是一个显而易见的起点。我以前从未使用过 aligned 或 alignof 关键字。我读过一些关于对齐的文章,但它的水平比我多年来一直使用的要低得多。有谁知道 user_key_payload.data 的对齐方式是否会弄乱关键数据的预期内存地址,以及如何找出我的预期数据的实际位置?
char data[0]
表示结构之后的指针。它在内核中广泛用于指示 header 之后的 variable-length 有效负载。
__aligned(__alignof__(u64))
将数据对齐到 8 字节(64 位)边界。这意味着,例如,如果在 struct user_key_payload
中存储所有字段所需的大小是 10 个字节,那么结构将被填充以占用 16 个字节 (2*8)。
因此,如果内核威胁数据 8 字节对齐,但您的模块没有,那么 "abcdef" 就是您正在读取的填充。
编译模块时,始终确保包含用于编译内核的 header 文件的相同版本。