C 错误记录(一): 记一次指针类型转换的 bug

28 Oct 2013

相关日记: C 错误记录(二): 指针伪赋值

我以前用 c 写程序时不太注意指针类型转换需要注意的问题, 直到前段时间犯了一个类型转换的错误。

这是一个 websocket 服务端的程序。根据 websocket 协议,websocket 的消息帧荷载长度范围是 64bit 以下。于是我把接收函数的一个返回接收长度的指针参数类型声明为 uint64_t *msg_len:

void ws_get_msg(const uint8_t *buf, const struct ws_frame_ctx *f_ctx,
		uint8_t *msg, uint64_t *msg_len)
{
	uint64_t i;
	const uint8_t *payload = buf + f_ctx->payload_offset;

	*msg_len = f_ctx->payload_len;
	if (f_ctx->is_masked)
		for (i = 0; i < f_ctx->payload_len; i++)
			msg[i] = payload[i] ^ f_ctx->masking_key[i % 4];
	else
		memcpy(msg, payload, f_ctx->payload_len);
}

我这样调用上面这个函数:

	ws_get_msg(buf->data, &frame_ctx,
		   conn->msg->data, (uint64_t *)&conn->msg->used);

conn->msg 的结构是这样的:

struct buf {
	size_t max;
	size_t used;
	uint8_t *data;
};

结果程序运行的时候,一直解析不了正确的消息,调试发现调用ws_get_msg()conn->msg->data这个指针变为 0 了。

原来我把这个指向size_t类型(32位)的指针&conn->msg->used作为参数传进ws_get_msg()后,这句:

	*msg_len = f_ctx->payload_len;

把紧挨着的*data成员覆盖为 0 了。

在《Linux 内核设计与实现》(第3版)19.4.1节列举了一个因对齐引发的问题,也是与指针类型转换相关。