2009年6月27日星期六

行人车辆请注意,飞机就要开过来了!

请在拦木外等候,不要抢行,不要翻栏杆!

Linux代码淬火工具

介绍用来提高Linux应用程序安全性和可靠性的开源工具和代码追踪技术。
一、源代码检查工具
在软件开发过程中,我们可以利用源码检查工具来找出常见的编程错误以及安全漏洞。这些工具用起来并不复杂,下面介绍splint和flawfinder这两款源码检查工具的使用方法。
splint是一款静态源代码检查工具,能对源代码进行全面的分析。对于没有注释的源代码,可以使用-weak选项:
splint -weak *.c -I./inc
其中,./inc是头文件所在的子目录。此外,Splint还支持标准检查模式(选项-standard),若要进行中等强度检查则使用选项-checks,若使用选项-strict则进行最严格的检查。
flawfinder也是一款用来寻找源代码错误的静态分析工具。通过该工具提供错误消息,开发人员可以更快的找到错误所在。请看下面的例子:
$ flawfinder test.c
test.c:11: [2] (buffer) char:
Statically-sized arrays can be overflowed. Perform bounds
checking, use functions that limit length, or ensure that
the size is larger than the maximum possible length.
$
本例中,flawfinder给出了一个提示,指出静态尺寸的数组可能被恶意利用的潜在危险。
除了上面介绍的splint和flawfinder这两款工具外,还有RATS(一款安全审计工具)以及ITS4(静态漏洞扫描工具)等工具可用。但需要
注意的是,虽然这些工具能够分担一部分工作,但却无法完全替代人类。因为工具在发现漏洞的同时,也可能遗漏安全漏洞。

二、代码跟踪技术
我们知道,strace工具通常是用来追踪系统调用的,实际上,它还可以作为间接的源代码审计工具。从系统调用的角度来跟踪应用程序的执行,可以让我们了解到Linux应用程序的底层操作,借助这些低层操作我们可以更好的理解我们的源代码。
在下面的例子中,有多处违反了我们前面讨论的代码淬火原则,现在我们展示如何利用strace来进行调试。
#include <unistd.h>
#include <fcntl.h>
#define MAX_BUF 128
int main()
{
int fd;
char buf[MAX_BUF+1];
fd = open( "myfile.txt", O_RDONLY );
read( fd, buf, MAX_BUF );
printf( "read %s\n", buf );
close( fd );
}
我们注意到,上面的代码的第11行,即:fd = open( "myfile.txt", O_RDONLY
);
试图打开一个称为myfile.txt的文件,但事前并没有检查该文件是否业已存在。在这种情况下执行该程序的话,会导致无法预测的结果:
$ gcc -o bad bad.c
$ ./bad
read @?8Z@
$
看看,这样的结果是你没料到的吧。所以,先让我们利用strace来看看到底发生了什么。注意,下面的输出已经作了删减,但重要的信息都保留下来了:
$ strace ./bad
execve("./bad", ["./bad"], [/* 20 vars */]) = 0
uname({sys="Linux", node="camus", ...}) = 0
...
open("myfile.txt", O_RDONLY) = -1 ENOENT ( No such file or
directory)
read(-1, 0xbfffef20, 128) = -1 EBADF (Bad file descriptor)
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0) = 0x40017000
write(1, "read \300\357\377\2778Z\1@\n", 14read /à???8z@
) = 14
close(-1) = -1 EBADF (Bad file descriptor)
munmap(0x40017000, 4096) = 0
exit_group(-1) = ?
$
执行程序后,我们看到用来启动该程序的系统调用是execve();不久又调用了open(),该系统调用对应于代码中的第11行。并且我们看到系统调用
open()的右边的返回值是-1,并指出错误"ENOENT ( No such file or
directory)",即不存在这个文件或目录。换句话说,这是在告诉我们需要先建立文件。此外,系统调用read()也以失败而告终,它的错误是非法
的文件描述符,因为open调用失败了。
strace工具不仅用来帮助理解有源代码的程序的行为,而且也对于没有源代码的程序也同样有效。因为,透过对系统调用的观察,我们能够在二进制级别来理解程序的行为。

三、小结
古人云,工欲善其事,必先利其器。借助于上文介绍的代码淬火方面的编码知识,用来提高Linux应用程序安全性和可靠性的调试工具,相信读者能够更快更好开发出安全可靠的高品质软件来。

编程利器——splint工具

到底需不需要编译器之外的独立的静态代码检查工具呢?这个问题'仁者见仁,智者见智'。但是有一个结论我想大家都会认可,那就是越是在开发周期早期发现的
Bug,修复它所付出的代价就越小。而像lint这样的静态代码检查程序恰恰是让Bug在早期阶段'显露原型'的绝佳工具,而追求'lint-
clean'[注1]境界的代码也向来是专家级程序员的嗜好。别忘了在'C专家编程'一书中曾经提到Sun
OS的内核一直是保持'lint-clean'状态的,这就是榜样!还等什么?赶快学呀!^_^

有人抱怨'不敢用lint工具,
太多的Warnings把快屏幕都淹没了!',不过高手一般不这么想,他会细心琢磨这些Warnings背后的'暗示',并和lint工具沟通,利用
lint工具提供的交互方法屏蔽掉一些经过分析认为不能成为错误的Warnings。久而久之,高手本身就成了一个lint程序,就能够很快的用肉眼发现
代码中的问题,并指出问题所在,如何解决!他还能告知如何嵌入一些Annotations从而避免让lint程序产生不必要的Warnings,这时这位
高手对语言和程序的理解就又提高了一个档次了。其实使用ling工具不仅仅是为了提早发现程序中的Bug,其使用过程有助于你加深对程序的认识和理解。的
确事实就是这样。

Splint就是一款强大而且应用广泛的开源lint工具。它的强大的代码检查能力固
然让人称道,但是让我更欣赏的却是它提供的'Annotations'机制。
Splint可以让程序员在自己的代码中嵌入相应的Anotations,这些Anotations作为Splint分析代码时的输入以帮助Splint
产生对程序员更有用的信息。下面是一些Splint的使用入门,更多详细信息请查看'Splint
manual'。

1、最简单的Splint使用方法
>> splint *.c

2、Splint输出Warnings的基本格式
<file>:<line>[,<column>]: message
[hint]
<file>:<line>,<column>: extra location information, if appropriate
我们可以使用'+/-<flags>'来自定义其输出格式,如'splint -showcol
*c',则Splint不会在输出信息中显示'列'信息。

3、使用flags控制splint的检查范围和输出格式
'+<flag>' -- 表明某个flag处于打开状态,如'+unixlib';
'-<flag>' -- 表明某个flag处于关闭状态,如'-weak';

4、使用.splintrc环境文件
如果不想每次使用splint的时候都手工输入一堆'+/-<flags>',那么你可以把这些'+/-<flags>'预先写
到.splintrc文件中,当splint执行的时候它会自动加上这些flags的。默认的flags设置在'~/splintrc'文件中,但是如果
一旦splint的当前工作路径下也有.splintrc文件,那么这个.splintrc文件中的flag设置会覆盖'~/splintrc'中的
flags设置,但是命令行中的flags设置是具备最高优先级的,它会覆盖前面提到的任何一个文件中的flags设置。

5、使用Annotations
对于'Annotations'的作用,Java程序员并不陌生,但是C程序员则对这个不是那么了解。C代码中的Annotations用来指导Splint生成恰当的代码检查报告。下面这个例子对比使用和不使用Annotations,Splint的输出的差别:
/* testlint.c */
void foo1() {
/*@unused@*/int *p = NULL;
}

void foo2() {
int *p = NULL;
}

splint testlint.c
Splint 3.1.1 --- 28 Apr 2003

testlint.c: (in function foo2)
testlint.c:6:7: Variable p declared but not used
A variable is declared but never used. Use /*@unused@*/ in front of
declaration to suppress message. (Use -varuse to inhibit warning)

Finished checking --- 1 code warning

可以看出没使用Annotation的函数foo2被给出Warning了。Splint的Annotations繁多,我们在平时做lint时可以多多接触。

'早用lint,勤用lint',这是C专家给我们的建议。'lint-clean'也许离你并不遥远。

[注1]
'lint-clean' -- 程序能够顺利通过lint程序的检查。

2009年6月25日星期四

Socket send函数和recv函数详解

1.send 函数
int send( SOCKET s, const char FAR *buf, int len, int flags );
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
该函数的第一个参数指定发送端套接字描述符;
第二个参数指明一个存放应用程序要发送数据的缓冲区;
第三个参数指明实际要发送的数据的字节数;
第四个参数一般置0。
这里只描述同步Socket的send函数的执行流程。当调用该函数时,
(1)send先比较待发送数据的长度len和套接字s的发送缓冲的长度,
如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;
(2)如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len
(3)如果len大于剩余空间大小,send就一直等待协议把s的发送缓冲中的数据发送完
(4)如果len小于剩余
空间大小,send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。
如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。
要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执
行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回
SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了。
2. recv函数
int recv( SOCKET s, char FAR *buf, int len, int flags);
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0。
这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,
(1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,
(2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以
在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),
recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

VIM显示十六进制出错

在linux下用vim打开jpg文件,使用%!xxd进行16进制显示时,文件头显示为"3f3f
3f3f 0011 0804"文件尾端显示为 "3f3f
0a";而同样的操作在windows下,就显示为"ffd8 ffc0 0011 0804"和 "ffd9
0a",这才是正确的jpeg文件头和文件尾标志。
很蹊跷 !
初时,我以为是jpeg在windows和linux下是不同的文件头,后来把jpg后缀去掉,就一个纯文件,现象依旧。考虑可能不是操作系统的差异了。
重新使用ghex打开jpeg数据查看,发现显示正常,为"ffd8 ffc0 0011
0804"和 "ffd9 0a",正确。
估计应该是vim的问题了。
3f的ascii码是?,那表示vim对文件头、尾没有正常解析,是不是和vim解析文件时用的编码格式有关系呢?
打开.vimrc配置项,屏蔽掉下面这句话:
set fileencodings=utf-8,gb2312,gbk,gb18030,ucs-bom
再用vim打开jpeg文件,显示"ffd8 ffc0 0011 0804"和 "ffd9 0a",
显示正确。
原来,为了支持识别和显示中文,我规定了vim的fileencodings,
当vim打开文件时,会使用规定的编码格式对数据进行解析,可惜jpeg的文件头FFD8、尾FFD9
不是任何一个中文的编码,vim找不到对应的中文字,就显示为??,即:3f3f。
至此,困惑全部打开。

2009年6月24日星期三

一个农夫给世界带来的变化

福莱明,一个很平常、还十分穷苦的苏格兰农夫。
有一天,正在地里干活的他,忽然听到附近的泥沼里有人发出求救的哭喊声。他赶紧放下手中的家什,飞跑着来到泥沼边――天哪!是一个小孩掉进了粪池里。于是,福莱明镇定、迅速地采取措施,立即把这个小孩从死亡边缘给救了回来。
第二天上午,有一辆崭新的、颇是气派的马车停在农夫家门口,从车中走出一位优雅的绅士。他进了农夫的茅屋,首先自我介绍是那个被救小孩的父亲,然后很诚恳地对农夫说:"此刻,我不知该和您说什么,我真的非常感谢您……您救了我孩子的生命,我一定得报答您!"农夫憨厚地笑着说:"不!先生。我不能因为救了您的小孩而接受报答。"
就在这时,农夫的儿子从外面走进屋来,绅士朝农夫问道:"那是您的儿子吗?"农夫显得很自豪地说:"是!他是我的儿子。"绅士沉思一会,继而严肃地对农夫说:"我们签定个协议,让我带他走,我要让他接受良好的教育。假如这孩子能象他父亲一样,我相信,他将来一定会成为一个更令您感骄傲的人。"农夫这次毫不犹豫地答应了。
就是这个农夫的孩子,后来从圣玛利亚医学院毕业,并成为举世闻名的福莱明・亚历山大爵士――盘尼西林的发明者。他在1944年受封骑士爵位,并在同年荣获诺贝尔奖。
几年后,那个绅士的儿子染上肺炎,是谁救活了他?盘尼西林。那绅士是谁?上议院议员丘吉尔。绅士的儿子是谁?英国最伟大的政治家――丘吉尔。
一个小小农夫的不算太大的善良举措,竟然给世界带来如此重大的变化,真是善莫大焉。

巴菲特:通胀时期该如何投资

  本文首次于1977 年5 月发表于《财富》杂志,在目前环境中,随着人们对未来通胀预期的升温,重读此文,重温巴菲特思想的火花,相信当代投资者仍将能从中受益。
  巴菲特认为,就经济本质而言,股票和债券事实上非常相似,股票不过是穿着股票的外衣来到华尔街化装舞会的"股本债券"。由于股票的本质与债券非常相似,股票受通胀的影响也非常大。通胀转移富裕股东的收入来改善工人福利的潜力并不大。要想产生更多的经济福利,就需要大幅增加实际资本,并且投资在现代生产设施中。此外,通胀影响实际资本积累,部分企业派息后业务扩张资金所剩无几,企业在派息与增发新股之间游戏,最终受伤的还是投资者。通胀对企业回报率的影响有普遍性,但需要大量有形资产支出来维持经营的企业往往会受通胀伤害较大;需要较少有形资产支出的企业受伤害程度较小;经济商誉高的企业受到的伤害也较小……
  股票和债券类似,在通胀的环境中表现不佳,这早已不是什么秘密。过去十年中(六七十年代),我们大多数时间都处在通胀环境中,对股票而言,这也是一个困难重重的时期。
  但是,人们对这段时期内股市问题原因的认识尚不够充分。通胀时期债券持有人面临的问题并不难理解。当美元价值逐月恶化,以美元计算收益和本金的证券将不会成为大赢家。
  人们长期以来认为股票与债券不同。许多年来,人们普遍认为股票可以对冲通胀风险。产生这一认识的根源是,股票代表的不是债权,而是具有生产设施的公司的所有权。投资者认为,不管政府如何印钱,公司的生产设施将能保留其实际价值。
  但是,为何事与愿违呢?我认为主要原因是,就经济本质而言,股票和债券事实上非常相似。
  我知道,对许多投资者而言,这一观点听起来有些奇怪,他们会迅速反驳道,债券的回报是固定的,股权投资的回报率每年起伏较大。但是,如果仔细考察公司在二战后的总回报,你会发现一个惊人的现象:股权回报率事实上变化不大。
  股票的息票是固定的
  二战后第一个十年中(1946-1955),道琼斯工业指数平均每年回报率为12.8%。接下来的十年中,这一数字是10.1%。其后的十年中,回报率为10.9%。但是,多年总体而言,账面价值回报率趋向于12%。在通胀年份里,没有迹象显示回报率大幅超过这一水平,在物价稳定的年份里也同样如此。现在,我们把这些公司看作生产企业,而非上市股票。假设企业的所有者按净值购买股票。那么,他们自己的回报也将是大约12%。因为这一回报率前后一致性相当高,我们可以合理地将它假设为"股本息票"。
  当然,在现实世界里,股票投资者不单单是买入并长期持有。相反,投资者为了在所投资公司的利润分配中分得更大一杯羹,相互之间争勇斗智。很明显,整体而言,这种纷纷扰扰徒劳无功,对股本息票无任何影响,只不过是降低了投资者应当分得的部分,因为频繁交易产生大量的摩擦成本,如咨询费、经纪佣金。活跃期权市场的存在导致摩擦成本进一步上升,这一赌场般的市场丝毫不能提高美国企业的生产率,但却需要数千人在此劳作。
  股票是永久性的
  另一个事实是,现实世界中股票投资者通常不是以账面价值买入股票的。有时他们能够以低于账面价值的价格购买,不过大多数时候买入价高于账面价值,这种情况发生时,12%的回报率面临的压力就更大了。随着通胀率上升,股本资本的回报并未上升。从本质上讲,买入股票得到的是具有内在固定回报率的证券,这和买入债券是一样的。
  当然,债券和股票的形式有重大差别。首先,债券终将到期,也许投资者需要长期等待,但是最终投资者将能够重新协定合约条款。如果目前或预期通胀率使得他过去的息票不足,他可以拒绝进一步游戏,除非现在供应的息票能重新激起他的兴趣。类似情形最近几年时有发生。
  但是,股票是永久性的。股票的到期日无限远。股票投资者锁定了美国企业所能挣得的收益,不管有多少。如果美国企业的回报率注定是12%,那么投资者必须接受这一回报率。股票投资者整体而言既不能退出,也不能重新商议契约条款。个别公司可能被出售或清算,公司可能回购股份。不过,总体而言,新股发行以及留存收益确保公司系统中的股本资本将增加。
  所以,我们给债券形式打一分。债券息票最终将被重新商议,股本"息票"不能。当然,我们要承认,长期来看回报率为12%的息票似乎并不需要大量修改。
  穿"股本债券"外衣的股票
  我们的股票不过是穿着股票的外衣来到华尔街化装舞会的"股本债券"(equity bond)。
  普通债券和上述回报率为12%的"股本债券"之间还有另外一个重大差别。通常情况下,债券投资人以现金形式收到全部息票,可以自由地进行再投资。相比之下,股票投资者的股本息票部分被公司留存,其再投资的回报率只能是公司的回报率。换句话说,公司每年12%的回报率部分派息,剩余部分被投资到公司中,再产生12%的回报率。
  高通胀、高利率时代――股票风光不再、投资者纷纷退出
  回过头来看,1946-1966 年间的投资者可以认为自己着实分得了满满的三大杯羹。第一,他们受益于当时远高于利率的公司股本回报率。第二,公司将股本回报的一大部分进行再投资并获得高回报率,通过其他途径是很难获得这么高的回报率的。第三,随着人们普遍认识到前两个优势,股票投资者还受益于股本资本的升值。这一点意味着,除了最基本的大约12%的公司股本资本回报外,投资者还获得了红利,这部分红利来自于道指的市净率从1946年的1.33 倍涨到1966 年的2.20 倍所带来的收益。估值上升的过程中,投资者暂时获得了超过其所投资企业内在盈利能力的回报率。
  到60 年代中期,大型投资机构终于发现了这一投资的"人间天堂"。但是,就在这些金融巨头开始争相买入股票时,我们进入了通胀加速上升、利率升高的年代。估值上升的过程开始反转,这一点也相当符合逻辑。利率上涨无情地降低了所有现有固定息票投资标的的价值。随着长期公司债券利率开始上升(最终升至10%左右),股票的12%股本回报率和再投资特权都显得黯然失色。
  人们认为股票比债券风险大,这一点无可厚非。虽然股本息票长期来看大致固定,但每年的确有波动。投资者对未来的态度受每年波动的影响相当大,虽然这种影响经常是错误的。股票风险大的另外一个因素是它们的到期日无限长。由于这层额外风险,投资者自然的反应是期望股本回报稳稳高于债券回报,比如说,如果债券的回报为10%,那么同一类型公司发行的股票12%的股本回报率就不能算作"稳稳"。随着差价缩减,股票投资者开始寻求退出。
  但是,股票投资者作为一个整体不能退出。他们所能做的是大量换手,产生大量摩擦成本,随后估值水平大幅下降,因为12%的股本息票在通胀环境中吸引力下降。过去10 年中,债券投资者经历了一系列的冲击,在这一过程中,他们发现任何息票水平――不管是6%、8%或10%――都并非是注定的,债券的价格也是会崩溃的。大部分股票投资者没有认识到他们也有一个"息票",对此,他们现在还处于认识过程中。
  企业提高赢利的五种方法在通胀环境中难以凑效
  我们是否必须认为12%的股本息票是一成不变的呢?是否有什么规律说公司的股本资本回报率在通胀率持续攀升的环境中不能向上调整呢?
  当然没有这样的规律。不过,另一方面,美国也不能随心所欲或通过制定法律来提高公司赢利。要提高股本回报,公司需要至少做到以下一点:(1)提高周转率,即销量与企业使用总资产的比率;(2)使用更便宜的杠杆;(3)使用更多杠杆;(4)降低所得税;(5)增加经营利润率。
  除了以上这些,增加普通股回报率恐怕别无他法。这些方法在通胀时期究竟能不能提高公司的股本资本回报率呢?下面我们将仔细阐述。
  第一,提高周转率。关于此,我们需要考虑三个主要资产类别:应收账款、存货、固定资产。
  应收账款增长与销量成比例增长,不管销售额增长是靠销量增长、还是靠价格上涨驱动。这一点没有提高空间。
  至于存货,情况就没这么简单了。长期来看,单位存货的趋势追随单位销量的趋势。但是,短期内,存货周转率可能因预期成本或瓶颈等因素的影响而上下波动。
  使用"后进先出"的存货估值方法在通胀时期有利于提高报告的周转率。当销售额因通胀而上升时,使用"后进先出"存货估值方法的公司的存货水平要么维持不变,要么跟随销量增长。但无论如何,金额周转率上升。
  20 世纪70 年代早期,公司使用"后进先出"的会计方法的潮流兴起,其结果是降低了公司报告的收益和应缴税额。这一趋势目前似乎放缓。然而,许多使用"后进先出"会计方法公司的存在以及其他公司跟随潮流的可能性将导致公司报告库存周转率进一步上升。
  通胀带来周转率上涨的幅度不大
  就固定资产而言,通胀率只要上升,其首先的结果便是提高周转率。这是因为销量会立即反映新的价格水平,而固定资产账上只能缓慢反映变化,也就是说,随着现有资产退役并且以新价格重置时,才能反映变化。很明显,公司的重置过程越缓慢,周转率上涨越多。但是,当公司的重置周期完成后,周转率将不再因此上涨。假设通胀率稳定,销量和固定资产将开始与通胀率等速一起上涨。
  总的来说,通胀能带来周转率一定程度的上升。其中一部分上升是肯定的,因为使用"后进先出"的会计方法;如果通胀加速,另一部分上升是可能的,因为销量上升快于固定资产上升。但是,通胀带来周转率的上升是有限的,其幅度不足以导致股本资本回报率的大幅上升。
  那么,使用便宜的杠杆是否能带来股本资本回报率增加呢?不大可能。高通胀率通常导致借贷成本增加,而非减少。通胀率飚升导致资本需求飚升,由于出借人对长期贷款合同的担忧日渐加深,贷款条件也越来越苛刻。但是,即使利率不会进一步上升,杠杆也会变得更贵,因为公司账面上债务的平均成本小于替代成本。现有债务到期后,需要被替代。整体而言,杠杆成本未来的变化对股本回报可能产生轻微的抑制作用。
  出借人是否愿意大量放贷?
  在通胀引起融资需求方面,具有讽刺意味的是,利润率高的公司需要相对较少的债务资本;但是利润率低的公司对举债似乎欲壑难填。出借人现在对这一问题的认识比以前清楚多了,因此也不大愿意出借资金给那些资本需求大、利润率低的公司让他们大幅增加杠杆。
  然而,在通胀环境中,许多公司未来似乎定会通过增加杠杆来提高股本回报率。管理层会这样做,因为他们需要大量的资本,他们希望不需削减派息或发行新股便能够获得大量资本,因为在通胀环境下发新股的吸引力并不大。因此,他们自然的反应便是不计成本地大举借债。
  但是,以目前利率增加贷款对提升股本回报率的作用与60 年代早期利率为4%的贷款所起的作用相去甚远。更糟糕的是,负债率上升还会导致信贷评级被下调,导致利率成本进一步上升。
  因此,除了前文所阐述的,上述原因也会导致杠杆成本上升。总的来说,杠杆成本上升的弊处可能抵消杠杆增加的益处。
  此外,美国公司系统的债务远高于传统资产负债表上显示的水平。许多公司有巨大的养老金义务,其数量与公司现任员工退休时的工资水平相挂钩。在1955-1965 年间低利率环境中,来自这些养老金计划的债务是可以合理预测的。如今,没有人能说得清楚公司最终义务有多少。
  当然,公司每年报表上都有一项关于未履行的养老金债务的精确数据。如果这一数据真实可靠,那么公司可以预先支付这一数目的金额,将其加入现有的养老金资产,将养老金资产全部交给一家保险公司,让它来承担公司现在所有的养老金债务。唉,可惜在现实世界里恐怕根本找不到一家愿意听一听这笔交易的保险公司。
  美国几乎每家公司的会计想到发行"生活费用"债券时都会忍不住要退缩。但是,通过由私营领域负担养老金的制度,美国的公司事实上已经背负起等同于该债券的数量惊人的债务。
  股东应当带着挑剔的眼光看待杠杆的增加,不管是传统债务,还是不入账的与物价挂钩的"养老金债务"。企业在不负债的情况下获得12%的回报率要远远好于债台高筑的企业获得同样的回报率。这意味着,如今公司12%的股本回报的价值要远低于20 年前12%的回报率的价值。
  降低所得税不大可行
  降低公司所得税似乎也不大可能提高股本资本回报率。美国公司的投资者所持有的股票只能算作D 类,A、B、C 类分别被联邦政府、州政府和市政府持有,代表的是他们征收所得税的权利。虽然这些"投资者"不会占有公司资产,但是他们在公司收益中分得一大部分,包括留存收益导致股本增加所产生的收益,留存收益本该归属D 类股东。
  这些A、B、C 类股票的另一迷人特点是,只要任何一类"股东"进行单边投票,那么他们在公司收益中占有的份额将会立即大幅增加,而且不需付款,比如,对A 类股票持有人(联邦政府)而言,只要国会采取行动即可。更有趣的是,有时其中一类股票持有人会投票追溯提高其占有企业的份额,纽约的公司1975 年就很沮丧地发现了这种情况。每当A、B、C 类"股东"投票提高他们在企业中占有的份额时,剩余归属D 类股东――普通投资者――的部分就将下降。
  展望未来,长期来看,认为A、B、C 类股票控制人会投票降低他们的份额是不明智的。D 类股票持有人可能将不得不努力维持他们自己的份额。
  FTC 统计显示通胀上升经营利润率下降
  上面我们提到的五种增加股本回报率的方法中,最后一种是提高经营利润率。一些乐观的人希望可以通过这种方法大幅提高股本回报。没有证据显示他们错了。但是,1 美元的销售额中只有100 美分,在我们得到剩余价值、税前利润前,这1 美元的销售额中还要扣除多项支出,主要项目包括劳动力、原材料、能源和各种除所得税外的其他税种。在通胀时期,这些成本的相对重要性似乎很难下降。
  而且,最近统计证据也不支持上述认为通胀时期利润率扩大的说法。在1956-1965 这段通胀水平相对较低的时期,联邦贸易委员会(FTC)每季度报告的制造业公司平均每年税前销售利润率为8.6%。1966-1975 年间,平均利润率是8%。换句话说,尽管通胀率大幅上升,公司利润率还是下降了。
  如果企业能够按照重置成本定价,利润率在通胀时期将扩大。但是,一个简单的事实是,尽管大多数大型企业的市场影响力得到人们普遍信任,但是他们未能做到按重置成本定价。重置成本会计方法几乎总是显示过去十年中公司利润大幅下滑。如果像石油、钢铁和铝等主要行业的垄断能力名副其实,那么我们只能认为他们的定价政策备受制约。
  上面可能提高公司普通股回报的五个因素都分析完了。在我看来,在通胀时期,这些方法的效果都不大。也许你在分析完之后更加乐观,但请记住,12%左右的回报率已经伴随我们多年了。
  未来收益三因子:账面价值和市场价值的关系、税率和通胀率
  也许你同意上述12%左右的股本回报率多多少少是固定的,尽管如此你仍希望在未来岁月中取得良好的回报。这一点也无可厚非,毕竟,很长时间以来,很多投资者的收益都不错。但是,你未来的收益将取决于三个变量:账面价值和市场价值的关系、税率和通胀率。
  我们先来简单地算一算账面价值和市场价值。当股票价格一直等于账面价值时,一切都非常简单。如果股票的账面价值是100 美元,平均市场价值也是100 美元,企业12%的收益将为投资者带来12%的回报。如果派息率是50%,投资者将获得6 美元的股息、并且从企业账面价值增加中获得另外6 美元,这6 美元自然会反映在该投资者所持股份的市场价值中。
  如果该股票的价格是账面价值的150%,整个情况就变了。投资者同样将收到6 美元的现金股息,但是其回报率只有成本的4%。企业的账面价值同样也将上升6%至106 美元,该投资者所持股票的市场价值类似地也上升6%至159 美元。但是,投资者的总回报――包括股票升值和股息――只有10%,低于企业原本12%的回报率。
  当投资者以低于账面价值的价格买入股票时,这一过程正好反过来了。比如,如果股票价格是账面价值的80%,假设收益和派息率和上例相同,那么股息收益率将为7.6%(6/80),股票升值6%,总回报为13.5%。换句话说,折价买股的回报率要高于溢价买股,这是常识。
  "二战"后,道琼斯工业指数的市场价值1974 年最低,仅为账面价值的84%,1965年最高,是账面价值的232%,大多数时间,这一比率高于100%。假设未来这一比率接近100%,这意味着股票投资者将获得企业全部12%的回报率,至少他们含通胀、含税收益率为12%。
  通胀是一种税,扣除后的收益有多少?
  通胀是一种比我们立法机构制定的任何税的破坏性都更大的税种。通胀税有吞噬资本的奇妙功能。
  如果我的通胀率假设接近正确,那么即使市场上涨,结果仍然令人失望。上月道指大约为920 点,相对于10 年前上涨了55 点。但是,经通胀调整后,道指下跌大约345 点――从865 点跌至520 点。即使是为了得到这一结果,道指成分公司中已经有大约一半的收益未派发给所有者,而是进行再投资。
  未来十年中,单单算上12%的股本息票、40%的派息率和目前110%的市净率,道指就将翻番。考虑到7%的通胀率,在1800 点出售股票的投资者在支付资本利得税后的实际状况要比现在贫穷的多。
  说到这里,我几乎可以听得见投资者对我的悲观预测的反应了。他们可能会认为,不管未来投资环境中面临什么样的困难,他们总会努力为自己交上一份优异的投资答卷。他们成功的可能并不大,当然,投资者整体而言,是不可能成功的。如果你认为你可以随心所欲买进卖出一只证券,并且收益超过通胀率,我倒愿意做你的经纪人,但不是合伙人。
  即使所谓的免税投资者――养老金、大学捐赠基金――也逃脱不了通胀税。如果我假设的7%的通胀率是正确的,那么大学会计应当把每年收益的前7%仅仅视作对购买力的补充。捐赠基金的回报率如果低于7%,他们实际上什么也没挣得。如果通胀率为7%、整体投资回报率为8%,那么,这些自认为免税的机构实际上支付了87.5%的"所得税"。
  通胀影响实际资本积累,派息后业务扩张资金所剩无几
  为了理解通胀对实际资本积累的影响,需要稍微计算一下。我们暂时回到前面提到的12%的股本资本回报,这一盈利水平扣除了折旧,应该允许现有产能的重置。
  我们假设一半的盈利以股息形式派发出去,剩余6%作为未来增长的股本资本。如果通胀率低,公司增长的很大一部分是实物产出的真实增长。因为在这种情况下,为了复制今年的实物产出,第二年投入到应收账款、库存和固定资产的资金必须增加2%,这样只剩下4%投资于实物产出的增量部分。也就是说,2%为虚幻金额增长提供资金,反映的是通胀;剩余4%为真实增长提供资金。如果人口增长1%,那么实际产出增加4%带来人均净收入增长3%。过去我们经济体的人均净收入增长基本上就在这个水平。
  现在我们来做另一个运算。如果通胀率是7%、派息政策和杠杆率保持不变,那么考虑通胀成分后,剩下的实际增长为零。12%的盈利中一半用于派息,剩下的6%全部用于为维持去年的业务量而增加的资金。许多公司在正常派息后实际上没有留存盈利为业务扩张提供资金,他们开始问,我们如何才能降低分红同时又不会惹怒股东?我给他们带来一个好消息,现在已经有这样的方案了。
  近年来,发电行业分红能力甚微,甚至没有。或者换句话说,如果投资者同意购买他们的股票,他们就有能力派息。1975 年,电力行业支付普通股股息33 亿美元,要求投资者返还34 亿美元。当然,他们略施小计,拆东墙补西墙,这样得以避免重蹈Con Ed 公司的覆辙。1974 年,Con Ed 公司非常干脆地告诉投资者无力派发股息。这家公司坦率但不明智的做法在资本市场上换来的是灾难。
  精明一点的公用事业类公司维持甚至增加季度派息水平,然后要求新老股东把钱汇过来。换句话说,公司发了新股。这种做法将巨额的资本送给税务部门和承销商。但是,似乎每个人都兴致高昂,特别是承销商。
  或许政府是解铃人
  随着企业努力应对实际资本积累问题,预计这种削减派息的方法将会被更广泛地使用。但是,控制股东并不能完全解决这一问题。7%的通胀率和12%的回报率导致用来满足公司未来真实增长所需的现金流减少。
  因此,随着传统的私人资本积累方法在通胀面前变得岌岌可危,我们的政府将越来越多地试图影响行业的资本流入,他们也许会像英国那样失败,或者像日本那样取得成功。美国不具备日本式的、政府、企业和劳工密切合作所需要的文化土壤和历史背景。如果幸运,我们或许能避免步英国后尘,在英国,各个集团围绕如何分配蛋糕而非协力做大蛋糕争斗不休。
  总之,随着时间推移,我们可能会听到更多关于投资不足、滞胀以及私人领域不能满足需求的故事。
  附:通胀环境中有望表现较好的公司
  既然通胀对企业盈利影响具有普遍性,那么哪些公司受伤害较小?在1983 年致股东信中,巴菲特指出,虽然长期以来人们认为那些自然资源、厂房、机器或其它有形资产丰富的企业最能提供通胀保护,但情况并非如此。任何需要有形资产净支出进行运营的无杠杆企业均将受到通胀伤害。需要很少有形资产支出的企业受通胀伤害最小。重资产企业通常回报率低,其回报率通常不足以满足通胀带来的企业现有业务运营的资金需求,没有剩余资金来支持真实增长、向股东分配股息、获得新业务。相比之下,在通胀岁月中积累的大量财富中,那些对有形资产需求较小、拥有持久价值的无形资产的企业占比较大。在通胀时期,经济商誉(而非会计商誉)能够持续创造价值,因为真实经济商誉的名义价值通常与通胀成比例增长。
  在1981 年致股东信中,巴菲特提到,其旗下伯克希尔哈撒韦公司收购的两类公司结果非常亮丽,其中一类恰好是能够很好地适应通胀环境的企业。这类企业必须有两个特点:(1)有能力在无需担心市场份额和单位产量大幅下降的情况下轻易地提价(即使当产品需求平稳、产能未得到充分利用时也能如此),(2)有能力将企业产出额大幅增加(更多是归因于通胀而非真实增长)与较少的额外资本投资需求协调好。