基于wolfSSL的DER转PEM批量转换函数段错误排查
DER转PEM时wolfSSL环境下的段错误排查
问题场景
在wolfSSL环境下实现DER到PEM的证书格式转换,使用以下C函数:
int convertDERtoPEM(const char* derFilePath, const char* pemFolderPath) { FILE* derFile = fopen(derFilePath, "rb"); if (!derFile) { printf("Error opening DER file %s\n", derFilePath); return -1; } unsigned char* derData = NULL; fseek(derFile, 0, SEEK_END); long derSize = ftell(derFile); rewind(derFile); derData = (unsigned char*)malloc(derSize); if (!derData) { printf("Memory allocation error\n"); fclose(derFile); return -1; } fread(derData, 1, derSize, derFile); fclose(derFile); WOLFSSL_X509* x509 = wolfSSL_X509_new(); if (!x509) { printf("Error creating X509 structure\n"); free(derData); return -1; } if (wolfSSL_d2i_X509(&x509, &derData, derSize) == NULL) { printf("Error converting DER to X509\n"); free(derData); wolfSSL_X509_free(x509); return -1; } const char* baseName = basename(derFilePath); char pemFileName[256]; snprintf(pemFileName, sizeof(pemFileName), "%s/C_%.*s.pem", pemFolderPath, (int)(strlen(baseName) - 4), baseName); FILE* pemFile = fopen(pemFileName, "wb"); if (!pemFile) { printf("Error opening PEM file %s for writing\n", pemFileName); free(derData); wolfSSL_X509_free(x509); return -1; } if (wolfSSL_PEM_write_X509(pemFile, x509) == 0) { printf("Error writing PEM certificate to %s\n", pemFileName); fclose(pemFile); wolfSSL_X509_free(x509); free(derData); return -1; } fclose(pemFile); wolfSSL_X509_free(x509); free(derData); printf("Conversion complete. %s converted to PEM and saved as %s.\n", derFilePath, pemFileName); return 0; }
问题现象
- 批量执行该函数时直接触发段错误,栈信息如下:
#0 0x00007ffff78a54c9 in arena_for_chunk (ptr=0x55555556317a) at ./malloc/arena.c:156 #1 arena_for_chunk (ptr=0x55555556317a) at ./malloc/arena.c:160 #2 __GI___libc_free (mem=<optimized out>) at ./malloc/malloc.c:3390 #3 0x0000555555555782 in convertDERtoPEM ( derFilePath=0x7fffffffde90 "./der/rights.1688.com_1.der", pemFolderPath=0x555555556121 "./pem_c") at DtoP.c:71 #4 0x00005555555558b2 in main () at DtoP.c:89
- 注释最后一行
free(derData)后,可完成部分证书转换,但处理某特定证书报错后仍触发段错误:
Conversion complete. ./der/rights.1688.com_1.der converted to PEM and saved as ./pem_c/C_rights.1688.com_1.pem. Conversion complete. ./der/alibaba-ipmarket.tmall.com_1.der converted to PEM and saved as ./pem_c/C_alibaba-ipmarket.tmall.com_1.pem. Conversion complete. ./der/xinyong.1688.com_1.der converted to PEM and saved as ./pem_c/C_xinyong.1688.com_1.pem. Conversion complete. ./der/movie.jd.com_1.der converted to PEM and saved as ./pem_c/C_movie.jd.com_1.pem. Conversion complete. ./der/hao123-static.cdn.bcebos.com_1.der converted to PEM and saved as ./pem_c/C_hao123-static.cdn.bcebos.com_1.pem. Error writing PEM certificate to ./pem_c/C_gys.1688.com_1.pem segfault
问题原因
核心问题是**wolfSSL_d2i_X509函数会修改传入的derData指针**:
该函数的第二个参数是unsigned char **类型,它在解析DER数据的过程中会移动这个指针,使其指向剩余未解析的字节。这就导致原来的derData不再是malloc返回的起始地址,后续对这个被篡改的指针执行free操作时,会触发堆内存错误,直接导致段错误。
即使注释了最后一行的free(derData),当处理特定证书出现wolfSSL_PEM_write_X509失败时,错误分支里的free(derData)同样会操作被篡改后的指针,因此仍然会触发段错误。
解决方案
保存malloc返回的原始指针,调用wolfSSL_d2i_X509时使用临时指针,确保后续free操作的是合法的堆起始地址。修改后的函数如下:
int convertDERtoPEM(const char* derFilePath, const char* pemFolderPath) { FILE* derFile = fopen(derFilePath, "rb"); if (!derFile) { printf("Error opening DER file %s\n", derFilePath); return -1; } unsigned char* derData = NULL; fseek(derFile, 0, SEEK_END); long derSize = ftell(derFile); // 增加ftell错误判断 if (derSize == -1) { printf("Failed to get DER file size\n"); fclose(derFile); return -1; } rewind(derFile); derData = (unsigned char*)malloc(derSize); if (!derData) { printf("Memory allocation error\n"); fclose(derFile); return -1; } fread(derData, 1, derSize, derFile); fclose(derFile); WOLFSSL_X509* x509 = wolfSSL_X509_new(); if (!x509) { printf("Error creating X509 structure\n"); free(derData); // 这里derData还未被修改,安全 return -1; } // 使用临时指针传递给wolfSSL_d2i_X509,保留原始derData指针 unsigned char* tmpDer = derData; if (wolfSSL_d2i_X509(&x509, &tmpDer, derSize) == NULL) { printf("Error converting DER to X509\n"); free(derData); // 原始指针,安全 wolfSSL_X509_free(x509); return -1; } const char* baseName = basename(derFilePath); // 检查baseName有效性,避免空指针 if (!baseName) { printf("Failed to get base filename\n"); free(derData); wolfSSL_X509_free(x509); return -1; } // 检查文件名后缀长度,避免strlen(baseName)-4为负数 if (strlen(baseName) < 4) { printf("Invalid DER filename format\n"); free(derData); wolfSSL_X509_free(x509); return -1; } char pemFileName[256]; snprintf(pemFileName, sizeof(pemFileName), "%s/C_%.*s.pem", pemFolderPath, (int)(strlen(baseName) - 4), baseName); FILE* pemFile = fopen(pemFileName, "wb"); if (!pemFile) { printf("Error opening PEM file %s for writing\n", pemFileName); free(derData); // 原始指针,安全 wolfSSL_X509_free(x509); return -1; } if (wolfSSL_PEM_write_X509(pemFile, x509) == 0) { printf("Error writing PEM certificate to %s\n", pemFileName); fclose(pemFile); wolfSSL_X509_free(x509); free(derData); // 原始指针,安全 return -1; } fclose(pemFile); wolfSSL_X509_free(x509); free(derData); // 原始指针,安全 printf("Conversion complete. %s converted to PEM and saved as %s.\n", derFilePath, pemFileName); return 0; }
额外注意事项
- 增加
ftell错误判断:避免ftell失败返回-1L后,malloc(-1L)触发非法内存分配。 - 校验
basename返回值:防止输入路径格式异常导致空指针访问。 - 检查文件名后缀长度:避免
strlen(baseName)-4出现负数,导致snprintf行为异常。
内容的提问来源于stack exchange,提问作者独揽双乔
相关产品推荐
相关产品推荐

