Malloc返回相同的值 - 没有双重自由错误

时间:2015-11-26 06:48:01

标签: c free undefined-behavior

查看最新更新

如果有以下功能,请记下通话的位置free(tmp)

int *power_arr(int *n, int nlength, int exp, int *res_length)
{
    int *tmp, *rt, *bufp;
    int bufp_length, i, dbg_i;

    rt = malloc(sizeof(int) * 1000);
    bufp = malloc(sizeof(int) * 1000);

    if (!rt || !bufp)
    {
        return NULL;
    }

    copy(rt, n, nlength);
    copy(bufp, n, nlength);

    *res_length = bufp_length = nlength;

    while (--exp > 0)
    {
        for (i = *n - 1; i > 0; i--)
        {
            tmp = sum(rt, *res_length, bufp, bufp_length, res_length);

            if (!tmp)
            {
                exit(-1);
            }

            copy(rt, tmp, *res_length);
           //free(tmp); // produces undefined output?
        }

        copy(bufp, rt, *res_length);
        bufp_length = *res_length;
    }

    free(tmp);

    free(bufp);

    return rt;
}

以下主要功能的结果:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    int b[] = { 3 };
    int r, i;
    int *rlength, *res;

    r = 0;

    rlength = &r;

    res = power_arr(b, 1, 3, rlength);

    printf("Length = %d\n", *rlength);

    for (i = 0; i < *rlength; i++)
    {   
        printf("i=");
        printf("%d\n", res[i]);
    }

    printf("\n");



    exit(0);
}

时:

Length = 2
i=2
i=7

我对第一个场景的理解是,每次后续tmp = sum(rt, *res_length, bufp, bufp_length, res_length);调用都会发生内存泄漏。在哪一点上,我决定将调用移到 for 循环内的free(tmp)。移动后,我注意到输出已经改变,如下所示:

Length = 4
i=1018670
i=4
i=2
i=7

我可以看到答案从我[3]开始。为什么free(tmp)调用的移动会产生这种影响?

我的理解是tmpfree()调用后成为悬空指针。然后,重新分配函数sum()返回的值 - 通过调用malloc()检索该值。在哪一点,通过将free()的呼叫置于其原始位置会发生内存泄漏。当tmp的值发生变化时,只有释放分配给它的最后一个指针。

编辑:

以下是支持功能的代码。

int *pad(int *n, int nlength, int new_length, enum SIDE side)
{
    int i, j;
    int *padded;

    if (nlength < 1 || new_length <= nlength)
    {
        return NULL;
    }

    padded = calloc(new_length, sizeof(int));

    if (!padded)
    {
        return NULL;
    }

    if (side == LOW)
    {
        j = new_length - 1;

        for (i = (nlength - 1); i >= 0; i--)
        {
            padded[j--] = n[i];
        }
    }
    else
    {
        j = 0;

        for (i = 0; i < nlength; i++)
        {
            padded[j++] = n[i];
        }
    }

    return padded;
}

int *trim(int *n, int nlength, int *res_length)
{
    int i, j;
    int *res;

    for (i = 0; i < nlength; i++)
    {
        if (n[i] > 0)
        {
            break;
        }
    }

    *res_length = (nlength - i);

    res = malloc(sizeof(int) * (*res_length));

    if (!res)
    {
        return NULL;
    }

    j = 0;

    while (i < nlength)
    {
        res[j++] = n[i++];
    }

    return res;
}

int *sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
    int i, tmp, carry;
    int *result, *trimmed, *op1, *op2;
    enum SIDE side = LOW;

    if (nlength == mlength)
    {
        op1 = n;
        op2 = m;
    }
    else if (nlength > mlength)
    {
        op1 = n;
        op2 = pad(m, mlength, nlength, side);
    }
    else
    {
        op1 = m;
        op2 = pad(n, nlength, mlength, side);
    }

    result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));

    if (!op1 || !op2 || !result)
    {
        return NULL;
    }

    carry = 0;

    for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--)
    {
        tmp = op1[i] + op2[i] + carry;

        if (carry > 0)
        {
            carry = 0;
        }

        if (tmp >= 10)
        {
            carry = tmp / 10;
            tmp = tmp % 10;
        }

        result[i + 1] = tmp;
    }

    if (carry > 0)
    {
        result[0] = carry--;
    }

    *sum_length = (MAX(nlength, mlength)) + 1;

    trimmed = trim(result, *sum_length, sum_length);

    free(result);

    if (!trimmed)
    {
        return NULL;
    }

    return trimmed;
}

void copy(int *to, int *from, int length)
{
    int i;

    for (i = 0; i < length; i++)
    {
        to[i] = from[i];
    }
}

更新

在实现第一篇文章中建议的更改后, double free 错误已经开始发生,要进行调试,我将以下打印语句添加到power_arr()。以下输出显示tmp正在从sum()分配与初始调用时收到的值相同的值。为什么呢?

更新了显示调试printf语句的代码:

    for (i = *n - 1; i > 0; i--)
    {
        tmp = sum(rt, *res_length, bufp, bufp_length, res_length);

        printf("first tmp = %d\n", tmp);

        if (!tmp)
        {
            printf("tmp was null\n");
            exit(-1);
        }

        copy(rt, tmp, *res_length);

        printf("second tmp = %d\n", tmp);\

        if (tmp != NULL)
        {
            printf("freeing tmp\n");
            free(tmp);
            tmp = NULL;
        }

        printf("tmp = %d\n", tmp);
    }

输出:

first tmp = 11227072
second tmp = 11227072
freeing tmp
tmp = 0
first tmp = 11227072 <-- Why has the pointer value not changed?
second tmp = 11227072
freeing tmp <-- Double free now occuring.
*** Error in `./a.out': double free or corruption (fasttop):        0x0000000000ab4fc0 ***
Aborted

更新

我相信我已将该错误追踪到trim()功能。我已经使用循环发布了该函数,以连续执行10次。正如您在输出中看到的那样,调用trim()的{​​{1}}会在后续调用中返回相同的指针值。然而,每次连续调用free都不会触发 double free 错误。为什么会这样?

malloc()

输出:

int main()
{
    int i, j, length;
    int n[] = { 4, 5, 6 };
    int m[] = { 0, 3, 5 };
    int *num;
    int *trimmed, *trimmed_length;

    trimmed_length = &length;

    for (i = 0; i < 10; i++)
    {
        num = (i % 2 == 0) ? n : m;

        trimmed = trim(num, 3, trimmed_length);

        if (!trimmed)
        {
            printf("trimmed was null\n");
            exit(-1);
        }

        for (j = 0; j < *trimmed_length; j++)
        {
            printf("%d", trimmed[j]);
        }   

        printf("\n");

        free(trimmed); 
    }

    exit(0);
} 

int *trim(int *n, int nlength, int *res_length)
{
    int i, j;
    int *res;

    for (i = 0; i < nlength; i++)
    {
        if (n[i] > 0)
        {
            break;
        }
    }

    *res_length = (nlength - i);

    res = malloc(sizeof(int) * (*res_length));

    if (!res)
    {
        return NULL;
    }

    j = 0;

    while (i < nlength)
    {
        res[j++] = n[i++];
    }

    printf("Returned pointer from trim() %d\n", res);

    return res;
}

这似乎也是我原始问题中的行为 - 触发双重免费错误。为什么在这种特殊情况下没有遇到 double free 错误?

2 个答案:

答案 0 :(得分:1)

你有许多内存泄漏点和一个完整的错误,这就是免费失败的原因[由于同一指针的双重释放]。

注意:我已对这个答案做了更新,但它太大了,不适合这里,所以我把它作为第二个答案发布了

我已将所有文件合并为一个所以我可以编译它[请原谅无偿的样式清理],修复了错误,并注释了所有的热点[这个编译,但我没有测试它]:

#include <stdlib.h>
#include <stdio.h>

enum SIDE {
    LOW
};

#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))

// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free"
#define FREEME(_ptr) \
    do { \
        if (_ptr != NULL) \
            free(_ptr); \
        _ptr = NULL; \
    } while (0)

int *
pad(int *n, int nlength, int new_length, enum SIDE side)
{
    int i,
     j;
    int *padded;

    if (nlength < 1 || new_length <= nlength) {
        return NULL;
    }

    padded = calloc(new_length, sizeof(int));

    if (!padded) {
        return NULL;
    }

    if (side == LOW) {
        j = new_length - 1;

        for (i = (nlength - 1); i >= 0; i--) {
            padded[j--] = n[i];
        }
    }
    else {
        j = 0;

        for (i = 0; i < nlength; i++) {
            padded[j++] = n[i];
        }
    }

    return padded;
}

int *
trim(int *n, int nlength, int *res_length)
{
    int i,
     j;
    int *res;

    for (i = 0; i < nlength; i++) {
        if (n[i] > 0) {
            break;
        }
    }

    *res_length = (nlength - i);

    res = malloc(sizeof(int) * (*res_length));

    if (!res) {
        return NULL;
    }

    j = 0;

    while (i < nlength) {
        res[j++] = n[i++];
    }

    return res;
}

int *
sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
    int i,
     tmp,
     carry;
    int *result,
    *trimmed,
    *op1,
    *op2;
    int padflg;
    enum SIDE side = LOW;

    // NOTE: this helps us remember whether to free op2 or not
    padflg = 1;

    // NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
    // function -- _this_ is the cause of the bug
    // case (1)
    if (nlength == mlength) {
        op1 = n;
        op2 = m;
        padflg = 0;
    }

    // NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
    // doesn't leak
    // case (2)
    else if (nlength > mlength) {
        op1 = n;
        op2 = pad(m, mlength, nlength, side);
    }

    // case (3)
    else {
        op1 = m;
        op2 = pad(n, nlength, mlength, side);
    }

    result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));

    if (!op1 || !op2 || !result) {
        if (padflg)
            FREEME(op2);
        FREEME(result);
        return NULL;
    }

    carry = 0;

    for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
        tmp = op1[i] + op2[i] + carry;

        if (carry > 0) {
            carry = 0;
        }

        if (tmp >= 10) {
            carry = tmp / 10;
            tmp = tmp % 10;
        }

        result[i + 1] = tmp;
    }

    // NOTE: we want to free op2 for case (2)/(3) but we didn't remember
    // how we got it: (1) means no free, (2)/(3) means free
    // only free if this if we called pad, and _not_ if this pointer belongs
    // to caller
    if (padflg)
        FREEME(op2);

    if (carry > 0) {
        result[0] = carry--;
    }

    *sum_length = (MAX(nlength, mlength)) + 1;

    trimmed = trim(result, *sum_length, sum_length);

    free(result);

    return trimmed;
}

void
copy(int *to, int *from, int length)
{
    int i;

    for (i = 0; i < length; i++) {
        to[i] = from[i];
    }
}

int *
power_arr(int *n, int nlength, int exp, int *res_length)
{
    int *tmp,
    *rt,
    *bufp;
    int bufp_length,
     i;

    // NOTE: rt/bufp are memory leaks -- they are never freed
    rt = malloc(sizeof(int) * 1000);
    bufp = malloc(sizeof(int) * 1000);

    // NOTE: this is a memory leak -- if one is null, but the other is non-null,
    // you must free the non-null one or it leaks
    if (!rt || !bufp) {
        FREEME(rt);
        FREEME(bufp);
        return NULL;
    }

    copy(rt, n, nlength);
    copy(bufp, n, nlength);

    *res_length = bufp_length = nlength;

    while (--exp > 0) {
        for (i = *n - 1; i > 0; i--) {
            tmp = sum(rt, *res_length, bufp, bufp_length, res_length);

            if (!tmp) {
                exit(-1);
            }

            copy(rt, tmp, *res_length);

            // NOTE: this will now work because of the padflg changes in
            // sum
#if 0
            // free(tmp); // produces undefined output?
#else
            FREEME(tmp);
#endif
        }

        copy(bufp, rt, *res_length);
        bufp_length = *res_length;
    }

    FREEME(bufp);

    return rt;
}

int
main(void)
{
    int b[] = { 3 };
    int r,
     i;
    int *rlength,
    *res;

    r = 0;

    rlength = &r;

    res = power_arr(b, 1, 3, rlength);

    printf("Length = %d\n", *rlength);

    for (i = 0; i < *rlength; i++) {
        printf("i=");
        printf("%d\n", res[i]);
    }

    printf("\n");

    exit(0);
}

答案 1 :(得分:0)

更新:我在这里添加第二个答案,因为新的代码示例太大,无法作为我之前的更新。

我发现了一些问题。如果重复运行原始测试程序,则每次运行时会给出不同的[错误]答案。也就是说,从脚本循环它。

我无法用现有的函数和参数修复剩余的bug。纯粹的复杂性增加了问题。

一旦我意识到你真正想做的事情(例如多精度数学),我就可以应用几种简化:

我没有到处传递int *vals, int *len,而是创建了一个“大号”struct
- 包含指向数据和长度的指针
- 数据缓冲区的长度可以动态增长/缩小 - 有参考计数

你在“大端”模式下进行数学运算。这使得有必要经常重新分配(例如在padcopy中)。这也增加了复杂性。我将其切换为使用“小端”,很多更容易使用。而且,它也有点[相当]更快。

我还在您的电源功能中注意到您使用sum来执行基本上rt += bufp;的操作。因此,我创建了一个版本sumeq,它直接在power rt值上运行,这进一步简化了事情。

注意:您仍然可以使用big endian,但我见过的大多数软件包都没用。仍然,创建/增长结构的基本例程是endian不可知的,你可以创建我创建的小端函数的大端版本[来自你的大端函数]

这是实际的代码。它是可构建的和可运行的。它将测试所有版本,包括您的原始代码[有问题]。

// badfree -- test program

#define TSTDEFALL
// badfree/badfree.h -- badfree control

#ifndef _badfree_badfree_h_
#define _badfree_badfree_h_

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#ifdef DEBUG
#define dbgprtf(_fmt...)        printf(_fmt)
#define dbgexec(_exec)          _exec
#else
#define dbgprtf(_fmt...)        /**/
#define dbgexec(_exec)          /**/
#endif

#define dbgleshow(_num,_who)    dbgexec(leshow(_num,0,_who))

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        fflush(stdout); \
        _sysfault(); \
    } while (0)

typedef unsigned int u32;

#if BIGDIG == 1
typedef char bigdig_t;
#elif BIGDIG == 2
typedef short bigdig_t;
#else
typedef int bigdig_t;
#endif
typedef bigdig_t *bigdig_p;
typedef const bigdig_t *bigdig_pc;

// multiprecision number control
struct bignum {
    u32 num_opts;                       // options
    const char *num_tag;                // symbol name
    int num_refcnt;                     // reference count
    int num_curlen;                     // current length
    int num_maxlen;                     // maximum length
    bigdig_p num_base;                  // pointer to vector
};
typedef struct bignum bgn_t;
typedef bgn_t *bgn_p;
typedef const bgn_t *bgn_pc;

// num_opts definitions
#define BGNASTRUCT      (1 << 0)    // struct was allocated
#define BGNABASE        (1 << 1)    // num_base was allocated
#define BGNINITAV       (1 << 2)    // bgninit -- init values
#define BGNINITDUP      (1 << 3)    // bgninit -- init values
#define BGNTRIM         (1 << 4)    // trim number
#define BGNRAW          (1 << 5)    // output number in raw order

#define BGNACQUIRE(_num) \
    bgnacquire(_num)
#define BGNRELEASE(_num) \
    _num = bgnrelease(_num)

#define BGNINIT(_sym,_opt,_len...) \
    _sym = bgninit(#_sym,_opt,_len)

#define BGNASSERT(_num,_opt) \
    do { \
        if (((_num)->num_opts & _opt) == (_opt)) \
            break; \
        sysfault("BGNASSERT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
            num,num->num_opts,_opt); \
    } while (0)

#define BGNASSERT_NOT(_num,_opt) \
    do { \
        if (((_num)->num_opts & _opt) != (_opt)) \
            break; \
        sysfault("BGNASSERT_NOT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
            num,num->num_opts,_opt); \
    } while (0)

enum SIDE {
    LOW
};

#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
#define MIN(_x,_y) (((_x) < (_y)) ? (_x) : (_y))

// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free" and use of pointer after it has been freed
#define FREEME(_ptr) \
    do { \
        if (_ptr != NULL) \
            free(_ptr); \
        _ptr = NULL; \
    } while (0)

// test control
struct tstctl {
    int tst_allow;                      // allow test to be performed
    const char *tst_tag;                // name of test
    void (*tst_fnc)(void);              // test function
};
typedef struct tstctl tstctl_t;
typedef tstctl_t *tstctl_p;
typedef const tstctl_t *tstctl_pc;

#define FORTSTALL(_tst) \
    _tst = tstlist;  _tst->tst_tag != NULL;  ++_tst

    int
    main(int argc, char **argv);

    void
    dotests(void);

    void
    dotest(tstctl_p tst);

    void
    usage(void);

    void
    becopy(bigdig_t *to, const bigdig_t *from, int length);

    bigdig_t *
    bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side);

    bigdig_t *
    betrim(const bigdig_t *n, int *res_length);

    bigdig_t *
    besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
        int *sum_length);

    bigdig_t *
    be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length);

    void
    betest_orig(void);

    void
    betest_trim(void);

    void
    betest_show(bigdig_pc num,int len,const char *sym);

    bgn_p
    bgninit(const char *tag,u32 opt,int maxlen,...);

    bgn_p
    bgnacquire(bgn_p num);

    bgn_p
    bgnrelease(bgn_p num);

    void
    bgngrow(bgn_p num,int newlen);

    void
    bgnshrink(bgn_p num);

    void
    lecopy(bgn_p to,bgn_pc from);

    bgn_p
    lepad(bgn_pc num,int newlen);

    void
    letrim(bgn_p num);

    int
    _letrim(bgn_pc num);

    void
    leshow(bgn_pc num,u32 opt,const char *who);

    void
    lesumeq(bgn_p result,bgn_p op2);

    bgn_p
    le_power_arr_eq(bgn_pc num,int exp);

    bgn_p
    lesumrt(bgn_p n,bgn_p m);

    bgn_p
    le_power_arr_rt(bgn_pc n, int exp);

    void
    letest_lesumrt(void);

    void
    letest_lesumeq(void);

    void
    _sysfault(void);

    void
    reverse(bigdig_p arr, int length);

#define TSTDEF(_on,_fnc) \
    { .tst_allow = _on, .tst_tag = #_fnc, .tst_fnc = _fnc }

#ifdef TSTDEFALL
tstctl_t tstlist[] = {
    TSTDEF(1,betest_orig),
    TSTDEF(0,betest_trim),
    TSTDEF(1,letest_lesumrt),
    TSTDEF(1,letest_lesumeq),
    { .tst_tag = NULL }
};
#endif

#endif

// badfree/bemath -- big endian math

void
becopy(bigdig_t *to, const bigdig_t *from, int length)
{
    int i;

    for (i = 0; i < length; i++)
        to[i] = from[i];
}

bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side)
{
    int i;
    int j;
    bigdig_t *padded;

    if (nlength < 1 || new_length <= nlength) {
        sysfault("bepad: length fault -- nlength=%d new_length=%d\n",
            nlength,new_length);
        return NULL;
    }

    padded = calloc(new_length,sizeof(bigdig_t));

    if (!padded)
        return NULL;

    if (side == LOW) {
        j = new_length - 1;

        for (i = (nlength - 1); i >= 0; i--)
            padded[j--] = n[i];
    }
    else {
        j = 0;

        for (i = 0; i < nlength; i++)
            padded[j++] = n[i];
    }

    return padded;
}

bigdig_t *
betrim(const bigdig_t *n, int *res_length)
{
    int i;
    int j;
    int nlength;
    bigdig_t *res;

    nlength = *res_length;

    for (i = 0; i < nlength; i++) {
        if (n[i] > 0)
            break;
    }

    nlength -= i;
    *res_length = nlength;

    res = malloc(sizeof(bigdig_t) * nlength);
    if (!res) {
        sysfault("betrim: null malloc\n");
        return NULL;
    }

    j = 0;
    for (;  i < nlength;  ++i, ++j)
        res[j] = n[i];

    return res;
}

bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
    int *sum_length)
{
    int i;
    int tmp;
    int carry;
    bigdig_t *result;
    bigdig_t *trimmed;
    bigdig_t *op1;
    bigdig_t *op2;
    int padflg;
    enum SIDE side = LOW;

    // NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
    // function -- _this_ is the cause of the bug
    // case (1)
    if (nlength == mlength) {
        op1 = n;
        op2 = m;
        padflg = 0;
    }

    // NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
    // doesn't leak
    // case (2)
    else if (nlength > mlength) {
        op1 = n;
        op2 = bepad(m, mlength, nlength, side);
        padflg = 1;
    }

    // case (3)
    else {
        op1 = m;
        op2 = bepad(n, nlength, mlength, side);
        padflg = 2;
    }

    result = malloc(sizeof(bigdig_t) * (MAX(nlength, mlength) + 1));

    if (!op1 || !op2 || !result) {
        sysfault("besum: null fault -- padflg=%d op1=%p op2=%p result=%p\n",
            padflg,op1,op2,result);
        if (padflg)
            FREEME(op2);
        FREEME(result);
        return NULL;
    }

    carry = 0;

    for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
        tmp = op1[i] + op2[i] + carry;

        if (carry > 0)
            carry = 0;

        if (tmp >= 10) {
            carry = tmp / 10;
            tmp = tmp % 10;
        }

        result[i + 1] = tmp;
    }

    // NOTE: we want to free op2 for case (2)/(3) but we didn't remember
    // how we got it: (1) means no free, (2)/(3) means free
    // only free if this if we called bepad, and _not_ if this pointer belongs
    // to caller
    if (padflg)
        FREEME(op2);

    if (carry > 0)
        result[0] = carry;

    *sum_length = (MAX(nlength, mlength)) + 1;

    trimmed = betrim(result, sum_length);

    FREEME(result);

    return trimmed;
}

bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length)
{
    bigdig_t *tmp;
    bigdig_t *rt;
    bigdig_t *bufp;
    int bufp_length;
    int i;

    // NOTE: rt/bufp are memory leaks -- they are never freed
    rt = malloc(sizeof(bigdig_t) * 1000);
    bufp = malloc(sizeof(bigdig_t) * 1000);

    // NOTE: this is a memory leak -- if one is null, but the other is non-null,
    // you must free the non-null one or it leaks
    if (!rt || !bufp) {
        FREEME(rt);
        FREEME(bufp);
        return NULL;
    }

    becopy(rt, n, nlength);
    becopy(bufp, n, nlength);

    *res_length = bufp_length = nlength;

    while (--exp > 0) {
        for (i = *n - 1; i > 0; i--) {
            tmp = besum(rt, nlength, bufp, bufp_length, res_length);

            if (tmp == NULL)
                sysfault("be_power_arr: null besum return\n");

            nlength = *res_length;
            becopy(rt, tmp, nlength);

            // NOTE: this will now work because of the padflg changes in
            // besum
#if 0
            // free(tmp); // produces undefined output?
#else
            FREEME(tmp);
#endif
        }

        becopy(bufp, rt, *res_length);
        bufp_length = *res_length;
    }

    FREEME(bufp);

    return rt;
}
// badfree/betest -- big endian tests

void
betest_orig(void)
{
#if 1
    bigdig_t b[] = { 3 };
#else
    const bigdig_t b[] = { 3 };
#endif
    int rlength;
    int exp;
    bigdig_t *res;

    exp = 3;
    printf("\n");
    printf("betest_orig: exp=%d\n",exp);

    betest_show(b,1,"b");

    rlength = 0;
    res = be_power_arr(b, 1, exp, &rlength);

    betest_show(res,rlength,"res");

    FREEME(res);
}

void
betest_trim(void)
{
    int i;
    int j;
    int length;
    const bigdig_t n[] = { 4, 5, 6 };
    const bigdig_t m[] = { 0, 3, 5 };
    const bigdig_t *num;
    bigdig_t *trimmed;

    printf("\n");
    printf("betest_trim:\n");

    for (i = 0; i < 10; i++) {
        num = (i % 2 == 0) ? n : m;

        length = 3;
        trimmed = betrim(num, &length);

        if (!trimmed)
            sysfault("betest_trim: trimmed was null\n");

        printf("pass %d: num=%p trimmed=%p\n",i,num,trimmed);
        for (j = 0; j < length; j++)
            printf(" %d", trimmed[j]);
        printf("\n");

        FREEME(trimmed);
    }
}

// betest_show -- show number
void
betest_show(bigdig_pc num,int len,const char *sym)
{
    int i;

    printf("  sym %s length %d --",sym,len);
    for (i = 0; i < len; i++)
        printf(" %d",num[i]);
    printf("\n");
}
// badfree/bgn -- big number control

// bgninit -- create new number
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...)
// opt -- options (BGNINIT* -- has initializer data)
// maxlen -- length of number
{
    va_list ap;
    int i;
    bgn_pc from;
    bgn_p num;

    va_start(ap,maxlen);

    num = calloc(1,sizeof(bgn_t));

    opt |= BGNASTRUCT;
    opt |= BGNABASE;
    num->num_opts = opt;

    num->num_tag = tag;

    num->num_refcnt = 1;

    if (maxlen <= 0)
        maxlen = 1;

    from = NULL;
    if (opt & BGNINITDUP) {
        from = va_arg(ap,bgn_pc);
        maxlen = MAX(maxlen,from->num_curlen);
    }

    num->num_maxlen = maxlen;
    num->num_base = calloc(maxlen,sizeof(bigdig_t));

    // initialize from varargs
    if (opt & BGNINITAV) {
        for (i = 0;  i < maxlen;  ++i)
            num->num_base[i] = va_arg(ap,int);
        num->num_curlen = maxlen;
    }

    // initialize by cloning data
    if (opt & BGNINITDUP) {
        maxlen = from->num_curlen;
        for (i = 0;  i < maxlen;  ++i)
            num->num_base[i] = from->num_base[i];
        num->num_curlen = maxlen;
    }

    va_end(ap);

    return num;
}

// bgnacquire -- increment reference count
bgn_p
bgnacquire(bgn_p num)
{

    num->num_refcnt += 1;

    return num;
}

// bgnrelease -- decrement reference count and deallocate
bgn_p
bgnrelease(bgn_p num)
{

    if (--num->num_refcnt == 0) {
        if (num->num_opts & BGNABASE)
            FREEME(num->num_base);

        if (num->num_opts & BGNASTRUCT)
            FREEME(num);

        // this zaps caller's pointer
        num = NULL;
    }

    return num;
}

// bgngrow -- grow allocated number to given length
void
bgngrow(bgn_p num,int newlen)
{
    int growlen;
    int maxlen;
    int i;

    BGNASSERT(num,BGNABASE);

    maxlen = num->num_maxlen;
    growlen = newlen - maxlen;

    if (growlen > 0) {
        maxlen += growlen;

        num->num_base = realloc(num->num_base,sizeof(bigdig_t) * maxlen);

        // zero extend the new area
        for (i = num->num_maxlen;  i < maxlen;  ++i)
            num->num_base[i] = 0;

        num->num_maxlen = maxlen;
    }
}

// bgnshrink -- shrink allocated number to current length
void
bgnshrink(bgn_p num)
{
    int curlen;

    BGNASSERT(num,BGNABASE);

    curlen = num->num_curlen;

    if (num->num_maxlen > curlen) {
        num->num_base = realloc(num->num_base,sizeof(bigdig_t) * curlen);
        num->num_maxlen = curlen;
    }
}
// badfree/lecom -- little endian math common

// lecopy -- copy big number
void
lecopy(bgn_p to,bgn_pc from)
{
    int newlen;
    int i;

    dbgprtf("lecopy: ENTER\n");

    dbgleshow(to,"lecopy");
    dbgleshow(from,"lecopy");

    newlen = from->num_curlen;

    bgngrow(to,newlen);

    for (i = 0; i < newlen; ++i)
        to->num_base[i] = from->num_base[i];

    to->num_curlen = newlen;

    dbgleshow(to,"lecopy");

    dbgprtf("lecopy: EXIT\n");
}

// lepad -- clone and pad number
bgn_p
lepad(bgn_pc num,int newlen)
{
    int i;
    int curlen;
    bgn_p padded;

    curlen = num->num_curlen;

#if 0
    if ((curlen < 1) || (newlen <= curlen)) {
        sysfault("lepad: length fault -- curlen=%d newlen=%d\n",curlen,newlen);
        return NULL;
    }
#endif

    BGNINIT(padded,0,newlen);
    if (!padded) {
        sysfault("lepad: bgninit returned null\n");
        return NULL;
    }

    // copy existing digits
    for (i = 0;  i < curlen;  ++i)
        padded->num_base[i] = num->num_base[i];

    // zero extend the larger number
    for (;  i < newlen;  ++i)
        padded->num_base[i] = 0;

    return padded;
}

// letrim -- get rid of leading zeroes by adjusting current length
void
letrim(bgn_p num)
{

    num->num_curlen = _letrim(num);
}

// _letrim -- get rid of leading zeroes by adjusting current length
// RETURNS: trimmed length
int
_letrim(bgn_pc num)
{
    int i;
    int curlen;

    curlen = num->num_curlen;

    for (i = curlen - 1;  i >= 0;  --i) {
        if (num->num_base[i] > 0)
            break;
    }

    if (i <= 0)
        i = 1;

    return i;
}

// leshow -- show number
void
leshow(bgn_pc num,u32 opt,const char *who)
{
    int curlen;
    int i;

    if (opt & BGNTRIM)
        curlen = _letrim(num);
    else
        curlen = num->num_curlen;

    if (who != NULL)
        printf("%s: ",who);

    printf("sym=%s ref=%d len=%d/%d",
        num->num_tag,num->num_refcnt,curlen,num->num_maxlen);

    printf(" trim=%s order=%s",
        (opt & BGNTRIM) ? "yes" : "no",
        (opt & BGNRAW) ? "raw" : "flip");

    printf("\n");

    if (who != NULL)
        printf("%s:",who);
    printf("  ");

    if (opt & BGNRAW) {
        for (i = 0; i < curlen; ++i)
            printf(" %d",num->num_base[i]);
    }

    else {
        for (i = curlen - 1;  i >= 0;  --i)
            printf(" %d",num->num_base[i]);
    }

    printf("\n");
}
// badfree/lesumeq -- little endian sum / power (in-place)

// lesumeq -- do x += y
void
lesumeq(bgn_p result,bgn_p op2)
{
    int op2len;
    int i;
    int tmp;
    int carry;
    int maxlen;
    int minlen;

    op2len = op2->num_curlen;
    tmp = result->num_curlen;
    maxlen = MAX(tmp,op2len);
    minlen = MIN(tmp,op2len);

    bgngrow(result,maxlen + 1);

    carry = 0;
    i = 0;

    for (;  i < minlen;  ++i) {
        tmp = result->num_base[i] + op2->num_base[i] + carry;

        if (tmp >= 10) {
            carry = tmp / 10;
            tmp %= 10;
        }
        else
            carry = 0;

        result->num_base[i] = tmp;
    }

    ++maxlen;

    for (;  i < maxlen;  ++i) {
        if (! carry)
            break;

        tmp = result->num_base[i] + carry;

        if (tmp >= 10) {
            carry = tmp / 10;
            tmp %= 10;
        }
        else
            carry = 0;

        result->num_base[i] = tmp;
    }

    result->num_curlen = maxlen;
}

// le_power_arr_eq -- raise number to power
bgn_p
le_power_arr_eq(bgn_pc num,int exp)
{
    bgn_p rtpwr;
    bgn_p bufp;
    int icur;
    int ilim;

    BGNINIT(rtpwr,BGNINITDUP,1000,num);
    BGNINIT(bufp,BGNINITDUP,1000,num);

    ilim = num->num_base[0];
    ilim -= 1;

    while (--exp > 0) {
        for (icur = 0;  icur < ilim;  ++icur)
            lesumeq(rtpwr,bufp);
        lecopy(bufp,rtpwr);
    }

    BGNRELEASE(bufp);

    return rtpwr;
}
// badfree/lesumrt -- little endian sum / power (alloc/return mode)

bgn_p
lesumrt(bgn_p n,bgn_p m)
{
    int i;
    int tmp;
    int carry;
    int maxlen;
    bgn_p rtsum;
    bgn_p op1;
    bgn_p op2;

    dbgprtf("lesumrt: ENTER\n");

    dbgleshow(n,"lesumrt");
    dbgleshow(m,"lesumrt");

    // case (1)
    if (n->num_curlen == m->num_curlen) {
        op1 = BGNACQUIRE(n);
        op2 = BGNACQUIRE(m);
    }

    // case (2)
    else if (n->num_curlen > m->num_curlen) {
        op1 = BGNACQUIRE(n);
        op2 = lepad(m,n->num_curlen);
    }

    // case (3)
    else {
        op1 = BGNACQUIRE(m);
        op2 = lepad(n,m->num_curlen);
    }

    maxlen = MAX(n->num_curlen,m->num_curlen);
    dbgprtf("lesumrt: PAD maxlen=%d\n",maxlen);

    BGNINIT(rtsum,0,maxlen + 1);

    carry = 0;

    for (i = 0;  i < maxlen;  ++i) {
        tmp = op1->num_base[i] + op2->num_base[i] + carry;

        if (tmp >= 10) {
            carry = tmp / 10;
            tmp %= 10;
        }
        else
            carry = 0;

        rtsum->num_base[i] = tmp;
    }

    rtsum->num_base[i] += carry;
    rtsum->num_curlen = maxlen + 1;

    BGNRELEASE(op1);
    BGNRELEASE(op2);

    dbgleshow(rtsum,"lesumrt");

    dbgprtf("lesumrt: EXIT\n");

    return rtsum;
}

bgn_p
le_power_arr_rt(bgn_pc n, int exp)
{
    bgn_p tmp;
    bgn_p rtpwr;
    bgn_p bufp;
    int icur;
    int ilim;

    dbgprtf("le_power_arr_rt: ENTER\n");

    BGNINIT(rtpwr,BGNINITDUP,1000,n);
    BGNINIT(bufp,BGNINITDUP,1000,n);

    ilim = n->num_base[0];
    ilim -= 1;

    while (--exp > 0) {
        for (icur = 0;  icur < ilim;  ++icur) {
            tmp = lesumrt(rtpwr,bufp);
            if (tmp == NULL)
                sysfault("le_power_arr_rt: null lesumrt return\n");

            lecopy(rtpwr,tmp);

            BGNRELEASE(tmp);
        }

        lecopy(bufp,rtpwr);
    }

    BGNRELEASE(bufp);

    dbgprtf("le_power_arr_rt: EXIT\n");

    return rtpwr;
}
// badfree/letest -- little endian tests

void
letest_lesumrt(void)
{
    bgn_p b;
    int exp;
    bgn_p res;

    exp = 3;
    printf("\n");
    printf("letest_lesumrt: exp=%d\n",exp);

    BGNINIT(b,BGNINITAV,1,3);
    leshow(b,0,"letest_lesumrt");

    res = le_power_arr_rt(b,exp);
    leshow(res,0,"letest_lesumrt");

    BGNRELEASE(res);
    BGNRELEASE(b);
}

void
letest_lesumeq(void)
{
    bgn_p b;
    int exp;
    bgn_p res;

    exp = 3;
    printf("\n");
    printf("letest_lesumeq: exp=%d\n",exp);

    BGNINIT(b,BGNINITAV,1,3);
    leshow(b,0,"letest_lesumeq");

    res = le_power_arr_eq(b,exp);
    leshow(res,0,"letest_lesumeq");

    BGNRELEASE(res);
    BGNRELEASE(b);
}
// badfree/util -- utility functions

void
_sysfault(void)
{

    exit(1);
}

void
reverse(bigdig_p arr, int length)
{
    int lhs;
    int rhs;
    bigdig_t tmp;

    for (lhs = 0, rhs = length - 1;  lhs < rhs;  ++lhs, --rhs) {
        tmp = arr[lhs];
        arr[lhs] = arr[rhs];
        arr[rhs] = tmp;
    }
}

int opt_fork;
int opt_T;

// main -- main program
int
main(int argc, char **argv)
{
    char *cp;
    tstctl_p tst;
    int tstno;

    --argc;
    ++argv;

    for (; argc > 0; --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'f':
            opt_fork = 1;
            break;

        case 'h':
            usage();
            break;

        case 'T':
            opt_T = atoi(cp + 2);
            break;

        default:
            usage();
            break;
        }
    }

    if (opt_T <= 0)
        opt_T = 1;

    for (FORTSTALL(tst))
        tst->tst_allow = (argc <= 0);

    for (; argc > 0; --argc, ++argv) {
        cp = *argv;
        for (FORTSTALL(tst)) {
            if (strcmp(tst->tst_tag, cp) == 0)
                tst->tst_allow = 1;
        }
    }

    for (tstno = 0; tstno < opt_T; ++tstno)
        dotests();

    return 0;
}

// dotests -- perform tests
void
dotests(void)
{
    tstctl_p tst;
    pid_t pid;
    int status;

    for (FORTSTALL(tst)) {
        if (!tst->tst_allow)
            continue;

        if (!opt_fork) {
            dotest(tst);
            continue;
        }

        pid = fork();
        if (pid) {
            waitpid(pid, &status, 0);
            continue;
        }

        dotest(tst);
        exit(0);
    }
}

// dotest -- perform test
void
dotest(tstctl_p tst)
{

    tst->tst_fnc();
}

// usage -- show usage
void
usage(void)
{
    tstctl_pc tst;

    printf("usage: [options] [test names]\n");

    printf("\n");
    printf("options:\n");
    printf("  -f -- run tests in forked child\n");
    printf("  -T<repeat> -- test repeat count\n");

    printf("\n");
    printf("tests:\n");

    for (FORTSTALL(tst))
        printf("  %s\n", tst->tst_tag);

    exit(1);
}