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

为何Bouncy Castle DTLS的ReceiveRecord返回值大于原缓冲区大小?

.NET CoAP DTLS服务器Bouncy Castle DTLS模块“internal error (80)”崩溃问题

搭建.NET CoAP DTLS服务器时,Bouncy Castle的DTLS模块持续崩溃并抛出“internal error (80)”错误。调试源码后定位到问题出在Org.BouncyCastle.Tls.DtlsRecordLayer的Receive方法中:

internal int Receive(Span<byte> buffer, int waitMillis, DtlsRecordCallback recordCallback)
{
    long currentTimeMillis = DateTimeUtilities.CurrentUnixMs();

    Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis);
    byte[] record = null;

    while (waitMillis >= 0)
    {
        ... // wait millis timeout checks

        int receiveLimit = m_transport.GetReceiveLimit();
        if (null == record || record.Length < receiveLimit)
        {
            record = new byte[receiveLimit];
        }

        int received = ReceiveRecord(record, 0, receiveLimit, waitMillis);

        int processed = ProcessRecord(received, record, buffer, recordCallback);
        if (processed >= 0)
            return processed;

        currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
        waitMillis = Timeout.GetWaitMillis(timeout, currentTimeMillis);
    }

    return -1;
}

核心问题是:负责解密记录并写入缓冲区的ProcessRecord方法,接收到的ReceiveRecord返回字节数大于传入的buffer缓冲区大小,导致写入溢出触发错误。对应的ReceiveRecord方法代码如下:

private int ReceiveRecord(byte[] buf, int off, int len, int waitMillis)
{
    if (m_recordQueue.Available > 0)
        return ReceivePendingRecord(buf, off, len);

    int received = ReceiveDatagram(buf, off, len, waitMillis);
    if (received >= RecordHeaderLength)
    {
        this.m_inConnection = true;

        ... // Epoch checks, to make sure what to read

        int recordHeaderLength = recordEpoch.RecordHeaderLengthRead;
        if (received >= recordHeaderLength)
        {
            int fragmentLength = TlsUtilities.ReadUint16(buf, off + recordHeaderLength - 2);
            int recordLength = recordHeaderLength + fragmentLength;
            if (received > recordLength)
            {
                m_recordQueue.AddData(buf, off + recordLength, received - recordLength);
                received = recordLength;
            }
        }
    }

    return received;
}

补充排查信息

最初怀疑是分片错误,但进一步确认ReceivePendingRecord仅在握手阶段触发,后续应用数据包不会走该分支。推测触发异常的记录并未分片,但通过Wireshark观察到崩溃总是发生在数据包长度异常的场景下,仍怀疑与数据长度处理逻辑相关。

问题分析与修复思路

  1. 缓冲区长度不匹配
    Receive方法中按m_transport.GetReceiveLimit()创建record数组,但未校验该长度是否超过传入的buffer大小。如果receiveLimit大于buffer.Length,ReceiveRecord返回的有效字节数可能超出buffer的容纳范围,导致ProcessRecord写入时溢出。

  2. 不完整记录未处理
    在ReceiveRecord中,仅当收到的字节数received大于解析出的recordLength时才截断并将多余数据加入队列,但未处理received < recordLength的情况(即收到不完整的DTLS记录)。这种场景下直接返回received,会导致ProcessRecord处理不完整数据,触发解密或解析错误。

  3. 修复方向

  • 校验缓冲区大小:在Receive方法调用ProcessRecord前,增加received与buffer.Length的校验,若超出则抛出明确错误或调整缓冲区:
    // 在调用ProcessRecord前添加
    if (received > buffer.Length)
    {
        throw new TlsFatalAlert(AlertDescription.internal_error);
        // 或根据业务场景选择丢弃当前记录、扩容缓冲区等逻辑
    }
    
  • 完善不完整记录处理:在ReceiveRecord中,当received < recordLength时,将当前数据加入m_recordQueue,返回-1等待后续数据:
    // 在计算recordLength后添加
    if (received < recordLength)
    {
        m_recordQueue.AddData(buf, off, received);
        return -1; // 表示需要继续接收数据以补全记录
    }
    
  • 检查Transport实现:确认m_transport.GetReceiveLimit()的返回值是否合理,是否与应用层传入的buffer大小匹配,避免创建过大的record数组。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 22:28:10