You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在R中调用C函数trimCovar时反复出现堆损坏(Heap Corruption)错误的排查求助

Heap Corruption in C Function Called from R (M1 Mac)

Update Notes

  • UPDATE3: 问题已解决,保留原始代码供参考——我已在下方发布了包含最终代码的回答,方便大家查看最终实现。
  • UPDATE2: 重构代码,使用R_alloc替代calloc以实现自动内存清理,但问题仍未解决。
  • UPDATE: 如果在UNPROTECT(1)前添加该行代码:Rprintf("%p %p %p", (void *)rans, (void *)fm, (void *)corrs);,函数执行时就不会出现堆损坏错误。推测可能是后台垃圾回收调用在执行完成前损坏了某个指针,导致写入无效指针。需要注意的是,如果不打印这三个指针地址,错误就会重现。另外,我在M1 Mac上运行代码,通过R CMD SHLIB使用clang编译,Apple硅芯片可能是问题诱因。

I'm stuck debugging a heap corruption issue and hoping the community can help out. I've written a C function trimCovar to optimize part of my R code, but repeated calls to this function from R trigger a heap corruption error. The function is invoked via .Call("trimCovar", ...).

Debugging Challenges

  • I'm on macOS, so I can't use Valgrind for memory debugging.
  • The C function relies on inputs from R, making it impossible to debug in isolation.
  • The heap corruption only occurs when the C function is called 10+ times within an R function—direct repeated .Call calls don't trigger the error.
  • The error doesn't occur at a fixed location in the code.

Function Context & Goal

I'm starting with two sets of vectors, which I condense into a frequency matrix: each column corresponds to a position in the vector sets, each row corresponds to a specific character value. To simplify preprocessing, I pass a combined frequency matrix for both vector sets. Here's an example of what that looks like:

Input Vectors:
v1_1 = 101, v1_2 = 011
v2_1 = 111, v2_2 = 110

Frequency Matrix:
position: | 1_1 | 1_2 | 1_3 | 2_1 | 2_2 | 2_3 |
0: 0.5 0.5 0.0 0.0 0.0 0.5
1: 0.5 0.5 1.0 1.0 1.0 0.5

The goal of trimCovar is to find the NV positions with the highest correlation between the two vector sets, calculated using pairwise KL divergence. Results are stored in an ascending-sorted linked list, and I only need to return the top NV entries' positions as a vector (duplicates allowed)—R handles the rest of the logic.

Function Parameters

  • fMAT: Combined frequency matrix (RObject, passed as a flat vector)
  • fSP: Column indices in the matrix corresponding to positions in the first vector set
  • sSP: Column indices in the matrix corresponding to positions in the second vector set
  • NV: Number of top correlated positions to return
  • NR: Number of rows in fMAT

Error Message

When the error triggers, I see this output:

R(95564,0x104858580) malloc: Heap corruption detected, free list is damaged at 0x600000f10040
*** Incorrect guard value: 4626885667169763328
R(95564,0x104858580) malloc: *** set a breakpoint in malloc_error_break to debug

Additional Observations

  • I suspect a dangling pointer is causing invalid memory references, but I can't pinpoint it.
  • Running gc() immediately after each call to the C function doesn't fix the issue.
  • Using lldb to debug, but I'm not familiar enough with it to get meaningful insights quickly.
  • Adding debug prints shows the error usually occurs in the main nested loop, but the trigger point isn't consistent.
  • I've saved the exact inputs that trigger the error, but running them in isolation doesn't reproduce the issue—only after repeated calls.

Code Implementation

#include <R.h>
#include <Rdefines.h>
#include <Rinternals.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct node {
 double data;
 int i1;
 int i2;
 struct node *next;
} node;
// Linked list
// data is the correlation value,
// i1 the position from first vector set,
// i2 the position from second vector set
node *makeNewNode(double data, int i1, int i2){
 node *newNode;
 newNode = (node *)R_alloc(1, sizeof(node));
 newNode->data = data;
 newNode->i1 = i1;
 newNode->i2 = i2;
 newNode->next = NULL;
 return(newNode);
}
//insert link in sorted order (ascending)
void insertSorted(node **head, node *toInsert, int maxSize) {
 int ctr = 0;
 if ((*head) == NULL || (*head)->data >= toInsert->data){
 toInsert->next = *head;
 *head = toInsert;
 } else {
 node *temp = *head;
 while (temp->next != NULL && temp->next->data < toInsert->data){
 temp = temp->next;
 if (ctr == maxSize){
 // Performance optimization, if we aren't inserting in the first NR
 // positions then we can just skip since we only care about the NR
 // lowest scores overall
 return;
 }
 ctr += 1;
 }
 toInsert->next = temp->next;
 temp->next = toInsert;
 }
}
// MAIN FUNCTION CALLED FROM R
// (This is the one that crashes)
SEXP trimCovar(SEXP fMAT, SEXP fSP, SEXP sSP, SEXP NV, SEXP NR){
 // Converting input SEXPs into C-compatible values
 int nv = asInteger(NV);
 int nr = asInteger(NR);
 int sp1l = length(fSP);
 int sp2l = length(sSP);
 int *firstSeqPos = INTEGER(coerceVector(fSP, INTSXP));
 int *secondSeqPos = INTEGER(coerceVector(sSP, INTSXP));
 double *fm = REAL(fMAT);
 int colv1, colv2;
 // Using a linked list for efficient insert
 node *corrs = NULL;
 int cv1, cv2;
 double p1, p2, score=0;
 // USUALLY FAILS IN THIS LOOP
 for ( int i=0; i<sp1l; i++ ){
 cv1 = firstSeqPos[i];
 colv1 = (cv1 - 1) * nr;
 for ( int j=0; j<sp2l; j++ ){
 cv2 = secondSeqPos[j];
 colv2 = (cv2 - 1) * nr;
 // KL Divergence
 score = 0;
 for ( int k=0; k<nr; k++){
 p1 = fm[colv1 + k];
 p2 = fm[colv2 + k];
 if (p1 != 0 && p2 != 0){
 score += p1 * log(p1 / p2);
 }
 }
 // Add result into LL
 node *newNode = makeNewNode(score, cv1, cv2);
 insertSorted(&corrs, newNode, nv);
 }
 R_CheckUserInterrupt();
 }
 SEXP ans;
 PROTECT(ans = allocVector(INTSXP, 2*nv));
 int *rans = INTEGER(ans);
 int ctr=0;
 int pos1, pos2;
 node *ptr = corrs;
 for ( int i=0; i<nv; i++){
 rans[2*i] = ptr->i1;
 rans[2*i+1] = ptr->i2;
 ptr = ptr->next;
 }
 UNPROTECT(1);
 return(ans);
}

I've only allocated linked list nodes, and I thought R_alloc would handle automatic cleanup. I've double-checked input bounds—99.99% sure I'm not accessing firstSeqPos, secondSeqPos, ans, or fm out of bounds. I've also reviewed the R code that calls this function repeatedly and don't see issues there. It's been a while since I wrote C, so I'm probably missing something obvious. I'd prefer not to switch to a Linux machine for Valgrind unless I have to—hoping there's another way to track this down. Thanks in advance for any help!


内容的提问来源于stack exchange,提问作者Aidan Lakshman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 21:23:10