ntpdate貌似有个Bug,但源代码里看不出来?

其它类软件,非上述版软件
回复
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

ntpdate貌似有个Bug,但源代码里看不出来?

#1

帖子 科学之子 » 2016-08-19 13:06

ntpdate貌似有个Bug,但源代码里看不出来?
测试前请注意当前系统时间正确与否是否重要
因为调整系统时间之后很可能出现服务器不可用的情况
遇到这种情况一般可以想办法换个IP去同步
比如我这里PPP方式联网就是断开重连
如果系统时间和服务器时间偏差太大导致此Bug被触发
可以添加"-b"参数来强制使用step(步进)方式同步

触发Bug的命令:

代码: 全选

date -s @$((`date +%s` +100000)); ./ntpdate  -p 1 cn.pool.ntp.org
但代码逻辑上貌似没有问题?
ntpdate.c代码:

代码: 全选

	if (always_step) {
		dostep = 1;
	} else if (never_step) {
		dostep = 0;
	} else {
		absoffset = server->soffset;
		if (absoffset < 0)
			absoffset = -absoffset;

			//由于前面的if,absoffset应该不可能是负数,但是我加上这段调试代码输出却发现absoffset是负数
			//absoffset为负数后就导致了dostep变量的数值异常,结果该"step"(步进)时却进行"adjust"(微调)了
			//absoffset的类型应该也没弄错,是int32,应该就是用%d输出吧?
			//msyslog这个函数我也没弄明白,只是照着printf猜的用法
			//但从ntpdate的外部行为推测,dostep变量确实发生了异常
			msyslog(LOG_NOTICE, "absoffset: %d \n NTPDATE_THRESHOLD :%d",absoffset,NTPDATE_THRESHOLD);
		dostep = (absoffset >= NTPDATE_THRESHOLD || absoffset < 0);
	}

	if (dostep) {
		if (simple_query || debug || l_step_systime(&server->offset)){
			msyslog(LOG_NOTICE, "step time server %s offset %s sec",
				stoa(&server->srcadr),
				lfptoa(&server->offset, 6));
		}
	} else {
#ifndef SYS_WINNT
		if (simple_query || l_adj_systime(&server->offset)) {
			msyslog(LOG_NOTICE, "adjust time server %s offset %s sec",
				stoa(&server->srcadr),
				lfptoa(&server->offset, 6));
		}
上次由 科学之子 在 2016-08-19 23:52,总共编辑 1 次。
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#2

帖子 科学之子 » 2016-08-19 14:00

下面是命令+输出

代码: 全选

# date -s @$((`date +%s` +100000)); ./ntpdate  -p 1 cn.pool.ntp.org
Sat Aug 20 17:45:52 CST 2016
20 Aug 17:45:52 ntpdate[4964]: absoffset: -2147483648 
 NTPDATE_THRESHOLD :32768
20 Aug 17:45:52 ntpdate[4964]: Can't adjust the time of day: Invalid argument
头像
vickycq
帖子: 4507
注册时间: 2011-03-20 13:12
系统: Debian
来自: 山东省寿光县
联系:

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#3

帖子 vickycq » 2016-08-19 14:25

或为 http://bugs.ntp.org/show_bug.cgi?id=3023

貌似已于 ntp 4.2.8p7 修复。在 stretch/sid (4.2.8p8) 中是否仍有此问题有待测试。
https://fossies.org/diffs/ntp/4.2.8p6_v ... -diff.html

看来此处问题多多,早在 2002 年就有类似问题:
http://marc.info/?l=ntp-bugs&m=103224904812746&w=2
Debian 中文论坛 - forums.debiancn.org
欢迎所有 Debian GNU/Linux 用户
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#4

帖子 科学之子 » 2016-08-19 17:12

vickycq 写了:或为 http://bugs.ntp.org/show_bug.cgi?id=3023

貌似已于 ntp 4.2.8p7 修复。在 stretch/sid (4.2.8p8) 中是否仍有此问题有待测试。
https://fossies.org/diffs/ntp/4.2.8p6_v ... -diff.html

看来此处问题多多,早在 2002 年就有类似问题:
http://marc.info/?l=ntp-bugs&m=103224904812746&w=2

代码: 全选

/* [Bug 3023] get absolute difference, avoiding signed
 * integer overflow like hell.
 */
改为无符号就能解决了吗?
问题是就算是有符号溢出了,按理说那个if也能保证不会出现负数啊
为什么运行过程就出现负数了呢?
难道是"Memory barrier"之类的原因?(只是知道那么个名词和抽象的定义,但具体是如何作用的还是不明白)
不过我还是看不透...
头像
astolia
论坛版主
帖子: 6443
注册时间: 2008-09-18 13:11

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#5

帖子 astolia » 2016-08-19 19:45

不就是一个典型的整型溢出嘛

代码: 全选

#include <stdio.h>
int main() {
    int a = 0x80000000;
    printf("%d %d\n", a, -a);
    return 0;
}
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#6

帖子 科学之子 » 2016-08-19 19:54

astolia 写了:不就是一个典型的整型溢出嘛

代码: 全选

#include <stdio.h>
int main() {
    int a = 0x80000000;
    printf("%d %d\n", a, -a);
    return 0;
}
但是前面有个if取绝对值

代码: 全选

if (absoffset < 0)
         absoffset = -absoffset;
按理就算溢出了也不应该出现负数?
到了计算dostep时,absoffset应该永远都是>=0
当然,以上仅教科书式判断,实际情况我就不清楚为什么异常了.
Fri Aug 19 19:56:47 CST 2016补充:
:Aadterboom
刚刚试了下,居然还有无法取反数的整型值?
这就是传说中的负0的实现?
头像
astolia
论坛版主
帖子: 6443
注册时间: 2008-09-18 13:11

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#7

帖子 astolia » 2016-08-19 20:16

这是任何计算机专业大一一进去就要学的计算机组成原理的内容之一:有符号整数的表示方式。一般会放在第二章,在第一章绪论之后
你可以看这篇,看到第四段就明白了 http://www.it610.com/article/414917.htm
至于负0,那是浮点数的内容,组成原理那一章同样会讲。自己找本书来看看就知道了
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#8

帖子 科学之子 » 2016-08-19 20:30

astolia 写了:这是任何计算机专业大一一进去就要学的计算机组成原理的内容之一:有符号整数的表示方式。一般会放在第二章,在第一章绪论之后
你可以看这篇,看到第四段就明白了 http://www.it610.com/article/414917.htm
至于负0,那是浮点数的内容,组成原理那一章同样会讲。自己找本书来看看就知道了
我记得某个教程(忘了是什么了)说过虽然补码的0表示是唯一的,但整型的二进制"负0"的形式会因为平台不同而有不同实现
感谢纠正

不过就算是INT_MIN,后面那个逻辑或也应该是结果为True?

代码: 全选

#include <stdio.h>
#include <limits.h>
int main() {
	int dostep=0;
    int a = 0x80000000;
    if(a<0)a=-a;
    
    dostep = (a >= INT_MAX || a < 0);
    //dostep输出非0值
    printf("%d\n",dostep);
    printf("%d %d\n", a, -a);
    printf("%d\n",INT_MIN);
    unsigned i=0;
    for(i=0;i<sizeof(a);i++)
    printf("%.2x\n",((unsigned char*)(&a))[i]);
    return 0;
}
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#9

帖子 科学之子 » 2016-08-19 20:48

照着nptdate里面那个宏展手动开了一下
结果还是输出非0值

代码: 全选

#include <stdio.h>
#include <limits.h>
int main() {
	int dostep=0;
    int a = 0x80000000;
    int b = INT_MIN;
    if(a<0)a=-a;
    
    dostep = (b >= ((0x10000) >> 1) || b < 0);
    //dostep输出非0值
    printf("%d\n",dostep);
    printf("%d %d\n", a, -a);
    printf("%d %d\n",b ,-b);
    unsigned i=0;
    for(i=0;i<sizeof(a);i++)
    printf("%.2x\n",((unsigned char*)(&a))[i]);
    return 0;
}
头像
astolia
论坛版主
帖子: 6443
注册时间: 2008-09-18 13:11

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#10

帖子 astolia » 2016-08-19 21:09

科学之子 写了:整型的二进制"负0"的形式会因为平台不同而有不同实现
这个平台是指处理器,根据这篇 https://en.wikipedia.org/wiki/Two%27s_complement 的说法,支持两种符号整数0的小型/微型机在1970年之后就基本绝迹了。所以现在只存在只有正整数0的平台。

我不明白你拿那段代码想说啥。 a == 0x80000000 == b == INT_MIN == -2147483648,-2147483648<0为真,dostep也就为真,即1,有什么奇怪的?
你该不是把shell脚本里的0为真和C里面的非零为真弄混了吧
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#11

帖子 科学之子 » 2016-08-19 22:55

astolia 写了:
科学之子 写了:整型的二进制"负0"的形式会因为平台不同而有不同实现
这个平台是指处理器,根据这篇 https://en.wikipedia.org/wiki/Two%27s_complement 的说法,支持两种符号整数0的小型/微型机在1970年之后就基本绝迹了。所以现在只存在只有正整数0的平台。

我不明白你拿那段代码想说啥。 a == 0x80000000 == b == INT_MIN == -2147483648,-2147483648<0为真,dostep也就为真,即1,有什么奇怪的?
你该不是把shell脚本里的0为真和C里面的非零为真弄混了吧
但是dostep为1的话,就应该是step(步进)而非adjust(微调)
我发那个代码就是想说明,ntpdate的表现和我发的代码的表现不同
我无法看出这是什么原因
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#12

帖子 科学之子 » 2016-08-19 23:38

好奇怪,一输出dostep值,程序就正常
不输出就好像dostep异常了一样,表现出错误行为
加上注释就出现那个Bug,去掉注释,输出dostep的值就正常了

代码: 全选

//msyslog(LOG_NOTICE, "\ndostep:%d\n",dostep);
	if (dostep) {
		if (simple_query || debug || l_step_systime(&server->offset)){
			msyslog(LOG_NOTICE, "step time server %s offset %s sec",
				stoa(&server->srcadr),
				lfptoa(&server->offset, 6));
		}
	}
头像
astolia
论坛版主
帖子: 6443
注册时间: 2008-09-18 13:11

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#13

帖子 astolia » 2016-08-19 23:47

但是dostep为1的话,就应该是step(步进)而非adjust(微调)
我知道你的想法了:看到报错信息是Can't adjust the time of day,在源码里搜了一下,发现是个l_adj_systime函数输出的,再从头搜一下,看到了一楼的代码段,发现是当dostep为0时调用的,加了点调试语句,结果dostep值为1,然后就懵了
我说你咋就不去看一眼l_step_systime()的实现呢?
科学之子
帖子: 2284
注册时间: 2013-05-26 6:58
系统: Debian 9

Re: ntpdate貌似有个Bug,但源代码里看不出来?

#14

帖子 科学之子 » 2016-08-20 21:55

astolia 写了:
但是dostep为1的话,就应该是step(步进)而非adjust(微调)
我知道你的想法了:看到报错信息是Can't adjust the time of day,在源码里搜了一下,发现是个l_adj_systime函数输出的,再从头搜一下,看到了一楼的代码段,发现是当dostep为0时调用的,加了点调试语句,结果dostep值为1,然后就懵了
我说你咋就不去看一眼l_step_systime()的实现呢?
确实没看.
不过为什么要看l_step_systime()的实现?
仔细看了一下,里面会调用l_adj_systime(而且貌似是在有特殊的宏定义时才调用)
但问题在于症状是l_step_systime()根本没有被调用.
虽然代码逻辑上确实应该被调用

我在l_step_systime()的第一行加上"fprintf(stderr,"正常\n");",它也没输出

奇怪的是如果clock_adjust里面在那个if上面加上输出代码,就会改变结果到正常结果

代码: 全选

//如果没有这个输出语句,就会调用adjust
//这个输出语句的存在使程序的行为变正常了,不知道为什么
fprintf(stderr,"dostep:%d\n",dostep);

	if (dostep) {
回复