问题排查:C++ exception with description “getrandom“ thrown in the test body
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。
文章目录
- Motivation
- Process
Motivation
最近在做一个复杂系统集成到 Bazel 的工作。
在编译链接通过后为了能后确保外部系统的每个编译单元都没有动态链接的问题,需要多写一些测试代码,把这些代码都执行到。
在执行其中一个测试时,在众多的错误中报出了一个没有明确指向原因的错误:
unknown file: Failure
C++ exception with description “getrandom” thrown in the test body.
问题是代码中并没有直接调用到getrandom
系统调用,其次也不知道为什么会报错。
Process
首先执行如下代码,开始gdb单元测试:
- bazel build --compilation_mode=dbg --test_timeout=60 xxxx/xxxxx/xxxxxx:test_xxx
- gdb xxxx/xxxxx/xxxxxx/test_xxx
从不多的错误信息可以看出时抛异常了,在GDB中执行如下指令拿到异常栈帧:
- catch throw
- r
- bt
这里可以看到原来是在boost
库抛异常了,对应的boost
库代码如下:
class random_provider_base
{
public:
//! Obtain entropy and place it into a memory location
//! \param[in] buf the location to write entropy
//! \param[in] siz the number of bytes to acquire
void get_random_bytes(void *buf, std::size_t siz)
{
std::size_t offset = 0;
while (offset < siz)
{
ssize_t sz = get_random(static_cast< char* >(buf) + offset, siz - offset, 0u);
if (BOOST_UNLIKELY(sz < 0))
{
int err = errno;
if (err == EINTR)
continue;
BOOST_THROW_EXCEPTION(entropy_error(err, "getrandom"));
}
offset += sz;
}
}
private:
static ssize_t get_random(void *buf, std::size_t size, unsigned int flags)
{
#if defined(BOOST_UUID_RANDOM_PROVIDER_GETRANDOM_IMPL_GETRANDOM)
return BOOST_UUID_RANDOM_PROVIDER_GETRANDOM_IMPL_GETRANDOM(buf, size, flags);
#elif defined(BOOST_UUID_RANDOM_PROVIDER_GETRANDOM_HAS_LIBC_WRAPPER)
return ::getrandom(buf, size, flags);
#else
return ::syscall(SYS_getrandom, buf, size, flags);
#endif
}
};
我这个时候其实并不敏感。
从现有信息看已经基本确定是getrandom
的问题了。
从[1]可以看到 getrandom
是在glibc 2.25
支持的
执行ldd --version
发现我的glibc
版本是2.28
,原则上是支持的。
写个demo:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/random.h>
#include <unistd.h>
int main() {
size_t num_bytes = 16;
unsigned char buffer[16];
ssize_t result = getrandom(buffer, num_bytes, 0);
if (result == -1) {
perror("getrandom failed");
std::cerr << errno << " :" << std::strerror(err) << std::endl;
return EXIT_FAILURE;
}
printf("Random bytes: ");
for (size_t i = 0; i < num_bytes; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
return EXIT_SUCCESS;
}
执行起来果然报错,输出为:
执行ldd a.out
看看动态库依赖
此时怀疑glibc
的.so
有问题,或者内核版本有问题
执行rpm -qf /lib64/libc.so.6
,是RHEL的官方包,那基本有问题的概率不大。
执行nm libc.so.6 |c++filt|grep random
确实看到了glibc
的getrandom
符号,Text Section,且是有效的
执行strace -ff -o strace.log ./a.out
,检查下可执行文件调用了哪些系统调用,确定是glibc
的问题还是内核的问题。
原来原始的报错信息是Function not implemented
,经过了几层异常后已经看不出来原始报错了,重新看了一遍文档getrandom
系统调用3.17才支持,我的开发机只有3.10,是使用docker构造编译环境的。
docker只是经过Namespaces隔离,Cgroups隔离,Security隔离的特殊进程,和宿主机共享内核,自然系统调用就失败了。
所以确定就是内核版本问题。
参考:
- man getrandom