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

Kotlin/Native cinterop调用libxml2时XPath表达式数据损坏问题

版本信息

  • Kotlin 1.9.22
  • libxml 2.9.14+dfsg-1.3
  • 操作系统:Ubuntu 23.10

问题详情

在小型Kotlin/Native应用中用cinterop调用libxml2,通过XPath提取元素文本和属性值。原本运行正常,数周后某测试突然报错提示XPath无效。给测试加循环后发现,迭代次数足够时,总会在同一个XPath表达式(4个中的一个)上失败。

可复现问题的最小代码:

@OptIn(ExperimentalForeignApi::class)
fun getElementTextFake(xml: String, xPathToNode: String): Either<XmlError, String> {
    val xPathCValues = xPathToNode.encodeToByteArray().toUByteArray().toCValues()
    val doc: xmlDocPtr? = xmlReadDoc(cur = xml.trim().encodeToByteArray().toUByteArray().toCValues(), URL = null, encoding = "UTF-8", options = 0)
    val xPathCtx: xmlXPathContextPtr? = doc?.let { xmlXPathNewContext(doc = it) }
    val xPathObj: xmlXPathObjectPtr? = xPathCtx?.let { xmlXPathEvalExpression(str = xPathCValues, ctxt = it) }
    val nodeSet: xmlNodeSetPtr? = xPathObj?.pointed?.nodesetval

    val result =
        if (nodeSet == null || nodeSet.pointed.nodeNr == 0) ElementNotFound(xPathToNode, xml).left() else "fakeElementText".right()

    xmlXPathFreeObject(xPathObj)
    xmlXPathFreeContext(xPathCtx)
    xmlFreeDoc(doc)

    return result
}

libxml2输出的错误信息:

XPath error : Invalid expression<br>
//*[local-name()='request']/*[local-name()='processing']P

传入的原始XPath表达式是:

//*[local-name()='request']/*[local-name()='processing']

可见表达式末尾被追加了字符P导致无效,其他XPath表达式均不含大写P。错误几乎总会在几百次迭代内出现,且测试为单线程运行。

问题原因与解决方案

核心问题:C字符串缺少终止符

Kotlin的encodeToByteArray().toUByteArray().toCValues()生成的是没有NULL终止符的C值数组,但libxml2的xmlXPathEvalExpression要求传入标准C风格字符串(以\0结尾)。当内存中刚好在XPath数组后面的位置有字符P时,libxml2会读取到这个额外字符,导致表达式无效。这种未定义行为会在多次迭代后随机触发,取决于内存布局。

修复步骤

  1. 给XPath字符串的字节数组追加0字节,确保生成的C值数组包含NULL终止符:
// 修复XPath的C字符串生成
val xPathBytes = xPathToNode.encodeToByteArray() + byteArrayOf(0)
val xPathCValues = xPathBytes.toUByteArray().toCValues()
  1. 同样,xmlReadDoc的cur参数也需要NULL终止符,做相同处理:
val xmlBytes = xml.trim().encodeToByteArray() + byteArrayOf(0)
val doc: xmlDocPtr? = xmlReadDoc(cur = xmlBytes.toUByteArray().toCValues(), URL = null, encoding = "UTF-8", options = 0)
  1. 简化方案:用Kotlin/Native提供的cstr扩展函数,自动处理NULL终止符,代码更安全简洁:
val xPathCValues = xPathToNode.cstr
val xmlCValues = xml.trim().cstr

以上修改可彻底解决内存越界读取导致的XPath表达式损坏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:46:30