snprintf的截断拼接

今年2月,我收到客户A的反馈,说上个月有几条运维会话没有关联上工单。经过排查,关联工单操作仅有两处。将会话与某条工单关联只需要将该工单的信息赋过去即可,SQL语句大致为:

UPDATE session SET worknote_id = X, worknote_desc = Y WHERE id = Z;

其中X和Y分别代表工单ID和工单描述,Z为待操作会话的会话ID。
逻辑很简单,代码不过5行,但我并没看出问题所在。考虑到程序是多线程的,且会在多个数据库之间切换访问,我在想是否因为这个原因导致偶尔出现的数据库操作失败,然而几周下来并没什么收获。

一个月后,客户B反馈了类似的问题,但这次更离谱:有一条工单居然和之前所有的会话都关联上了!出于问题的严重性,客户要求一周内必须找出原因并予以解决,我只好硬着头皮返回去看那几行代码:

char sql[1024];
snprintf(sql, sizeof(sql), "UPDATE session SET worknote_id = %d, worknote_desc = '%s' \
                            WHERE id = %d", worknote_id, worknote_desc, sid);
sql_exec(sql, &error_info);

难道是因为worknote_desc中含有单引号?有也无妨,前面已经做过转义处理。
莫非sql缓冲区长度不够?为了排除这种可能,我索要了出问题的会话和工单数据库记录,发现居然有400多个中文字符!

至此,真相逐渐浮出水面:

教训

Table of Contents