2012年2月10日

Sudo format string vulnerability

0 コメント

前置き

Sudo format string vulnerability ( CVE-2012-0809 )  というバグが、sudo コマンドには存在する。CVEが出ていることからも判るように、結構深刻なバグで、セキュリティ・ホールとしての性質を伴う。sudoのページによれば
運が良くても sudo はクラッシュし、
運が悪いと攻撃者に root 権限を掌握される問題が含まれているかもしれない
との事だ。

で、このバグ、何がどうなっているのかについては、『てきとうなメモ: [Linux][Security] sudo-1.8のバグ』が詳しい。とても詳しいので、そちらだけ見ておけばいいやと思っていたのだが、何箇所かから、
お前も書け
という圧力を食らったので、書くことにする。ただし、新しい情報がなにかあるって言うわけじゃない。

影響範囲

このバグは sudo の 1.8.0 で導入され 1.8.3-p2 で修正されました。なので、影響は 1.8.0 - 1.8.3-p1 と言うことになります。

で、商用 Linux distribution の中で、影響があるのはなさそうです。
Free distribution の中も含めると、Fedora16, OpenSuSE 12.1, Rawhide の3つが影響を受けているようです。
(http://rpm.pbone.net/index.php3 でどのdistributionがどのrpmを使っているのか、分かります)

ただし、それ以外のOSでも sudo は使っているはずです。それらが安全かどうかは判りません。一応、sudo のページには対応している各種OS用の置き換えバイナリは用意されているようです。

という訳で、ほぼ影響はない、と言うことなので、以下は笑い話として。他人の失敗は蜜の味 (^w^)。

攻撃方法

攻撃方法は次のとおりです。
  1.  ln -s /sbin/sudo /tmp/%s
    のようにして、sudu コマンドを「%s」というファイル名にすり替える
  2. /tmp/%s を実行する

%s は printf() 系処理が format 文として利用しているものであれば、大抵のもので問題が生じるようです。どれでどのような症状が出るのかは、CPUなどにある程度依存します。


こうすると、argv[0] 文字列の中に %s という文字列が含まれるようになります。


技術的概略

このバグは、すごく簡単に言うと printf() 系関数を2段階かけた事に起因したバグです。printf() 系関数を多段に使うことは、セキュリティを考慮したコーディングとしては非常にまずいもので、
絶対やるな
と言っても構わないぐらい、危険な行為です。

修正後も2度使っていますが、まぁ、これならしょうがないかな、という所にまで治っています。

実はもう一つ、例外処理が足りていない、と言う問題もあります。sudoだから発生する確率はあまり高くありませんが…仮想メモリシステムを使い切った状態だと危険ですね。

詳細

sudo-1.8.3-p1 の ./src/sudo.c の最後に次のような関数があります。デバッグログを出力するためのコードですが、ここに悪さの元が含まれています:

void
sudo_debug(int level, const char *fmt, ...)
{
    va_list ap;
    char *fmt2;

    if (level > debug_level)
 return;

    /* Backet fmt with program name and a newline to make it a single write */
    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);
    va_start(ap, fmt);
    vfprintf(stderr, fmt2, ap);
    va_end(ap);
    efree(fmt2);
}

通常、argv[0] は "sudo" という文字列へのポインタです。で、getprogname() は argv[0]を返すようになっています。
そこで、
     fmt = "%s"
   fmtの次の引数 = "hello"
のような場合について考えてみましょう。


    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);


この行を通った段階で fmt2 は


    fmt2 = "sudo: %s\n";



になります。で、その後:


    vfprintf(stderr, fmt2, ap);


という行を通ると、stderr には


    "sudo: hello\n";


という文字列が送りつけられることになります。
何も問題はありませんよね??

では、攻撃方法にあるように sudo コマンドに対してリンクを貼って './%s' という名前で起動したらどうなるでしょう?

argv[0] = "./%s"

になります。



    easprintf(&fmt2, "%s: %s\n", getprogname(), fmt);


この行を通った段階で fmt2 は


    fmt2 = "./%s: %s\n";



になります。で、その後:


    vfprintf(stderr, fmt2, ap);


という行を通ると、stderr にはまず、第1引数である "hello" が出るので:


    "./hello: 



…おや、困りました。後半の %s のための文字列へのポインタが引数として渡されていません。しかも vfprintf() はそんな事を知りません。しょうがないので、stack 上にある値をポインタと解釈してしまいます。

運がよいと、「Segmentation Violation」が発生します。で sudo はクラッシュします。あぁ、core を吐かないように設定しているといいんですが…。運が悪いと、coreファイルを持っていかれますが、そこには /etc/shadow の値が書いてあるかもしれません。
まぁ、意図的にやっている奴らは
確実に core 取得できるように
設定してから実行するよね。
はっはっはっ

まぁ、運良く大した情報が保存されていない core ファイルが出来上がることを祈るしかありませんな。


ちなみに。"%s" は上記のとおりですが、"%n" という楽しい引数がございましてな…これ、引数で渡したアドレスにそこまで書いた文字数を「書きこむ」んですわ… はっはっは …

判ったと思いますが、万が一これらのバージョンを使っていたら
血の涙を流してでも upgrade しろ!!

対策

1.8.3-p2 はこう変更されました。
void
sudo_debug(int level, const char *fmt, ...)
{
    va_list ap;
    char *buf;

    if (level > debug_level)
 return;

    /* Bracket fmt with program name and a newline to make it a single write */
    va_start(ap, fmt);
    evasprintf(&buf, fmt, ap);
    va_end(ap);
    fprintf(stderr, "%s: %s\n", getprogname(), buf);
    efree(buf);
}

えぇ、見ての通り printf は相変わらず2度使われています。

ただ、1度目は与えられたフォーマットと引数から、とりあえず buf という文字列を作るために、2度目はargv[0] と buf を fprintf() で出力するために使われます。

fmt は sudo のプログラム内で指定するフォーマット文ですし、fprintf() で使われるフォーマット文もプログラム内で指定するフォーマット文ですので、取り敢えず引数に %s などの危険な記号が含まれていても安全、とは言えるでしょう。


ただね。このコード、 buf == NULL だった場合を考慮していないんですわ。一応、glibc の場合 NULL を渡されると "(null)" という文字列を出しますけどね、これは必須ではない。たとえばSolarisは core dump するそうです。

環境によっては fprintf() の例外処理の定義が甘い所につけ込んだコードになる危険性は残されています。

2011年12月29日

sched_clock() overflow after 208.5 days in Linux Kernel

19 コメント
えーっと、久しぶりに Linux Kernel にダメダメなバグが発見されて、よりにもよってうちの製品も影響を受けたので、ここに詳細を書くことにした。
つーか。新しい Kernel を使うなら皆で使おうよ。なんだよその「1つだけ」影響を受けて残りは「影響も受けないぐらい古い」ってのは…


概要
大雑把に 208.5日連続運転した Linux Kernel が突如として reboot する。

実機でなおかつ Time
Slice
Stamp Counter を内包している必要があるので、Pentium4以降のプロセッサ(が、それはようするに今ある Intel 系CPU全部)か、その互換CPUである必要がある。32bit モード、64bit モードの区別はない。
逆に VMware や Xen など、仮想マシン上で動いている kernel に影響はない。これはそもそもバグを内包したルーチンを、仮想マシンで動いていると検出した Linux Kernel は使わなくなるからだ。

一切の予防的挙動は取られていないので、file systemなども含めて、non-volatile メディアに対する影響は甚大かつ破壊的。回避策はパッチの当たった Kernel をインストールしてrebootするか、200日以内に計画的に reboot する事のみ。


影響範囲
Linux Kernel 2.6.28 で導入されたパッチにバグがあり、3.1.5 で修正された。この間にリリースされた distribution は多分全部影響を受ける。

なお、具体的には
2.6系列:  2.6.32.50でfix
3.0系列:   3.0.13でfix
3.1系列:   3.1.5でfix
らしい。

商用 distribution で影響を受けているもの。

まずは SuSE11 SP1。fix パッチはこうなっている:
http://kernel.opensuse.org/cgit/kernel-source/commit/?id=413af9e6f17a1a1519cae0bfd1c1fe4409bb42a3

RHEL6も 2.6.32 base らいいのでこれも影響を受ける。
(2012/02/14追記: RHEL6 は kernel-2.6.32-220.4.2.el6 で修正が入ったらしい。http://rhn.redhat.com/errata/RHBA-2012-0124.html
ただし、ひどい事に「2012年1月24日に登録され、fix ができたのは2012-02-13という事になっている。3ヶ月近く何をしていたのか…)

多分Fedoraとかも影響をうけるはずだが、マシンを上げていないので判らない(何その手抜き)。
Fedora マシンを上げたら
「3.1.1→3.16 に kernel を更新」
という update が出ていた。 Fedora16使いは速攻で上げることをお勧めする。
また、Fedora15使いは速攻で Fedora16 にして、update を最新に持っていくことをおすすめする。


3.1.5のバグ修正後のコードはこう:
http://lxr.linux.no/linux+v3.1.5/arch/x86/include/asm/timer.h#L58
(LXRとか、こうやってソースコード丸ごと指定できるからありがたい)



なお、VMware とかの仮想環境では、このバグっているルーチンはそもそも利用されない。なのでこれは実機上で動かしている Linux Kernel でのみ、発生する。なので、VMで実験を…とかは一切効かない。


技術的詳細
最近の Intel processor やその互換CPUには Time
Slice
Stamp Counter (TSC) という 64bit レジスタがある。

こいつは Intel の場合だと CPU 駆動クロックでカウントアップしていて、1tick ごとに+1していく。例えば 2GHz のCPUの場合、1秒で2Gづつ増えていくのだ。

また、AMDの場合だとCPUに投入されるクロックで 1tickごとに+1していく。大抵の場合は 800MHz なので1秒で800Mづつ増える。

判ると思うが、ほぼ nano 秒オーダーの時計として使えるので、リアルタイム制御をしたい場合、TSCはとてもありがたい。しかし、最近の電力消費量制御機構は、CPU駆動クロック周波数を変更してしまう。すると TSC の値は何がなんだか判らなくなる…
そこで、Linux Kernel 内部には cycles_2_ns() という関数があって、TSCの値をナノ秒に修正してくれる事になっている。

しかし、不幸はここに内包されていた


すごく簡単に言うと、TSCの値を必要な Scale Factor 倍すれば nano sec になる。しかし、TSCの値は nano sec に近い。当然 Scale Factor は 1.0 に近い、微妙な浮動小数点になってしまう。

そうだ! Scale Factor を分数で表そう!!
SC/1024 で表現して、
SCを整数にしておけば、
SCを掛け算した後、
右に10bit シフトすれば済むよっ!!!

…この段階で何か嫌な予感がしなかったんだろうか、こいつらは…


とにかく、元の式は、TSCの値を 64bit 変数に代入し、それに 32bit のSC ( コード上は per_cpu() という関数の戻り値) を掛けて、10bit 右にシフトする、というものになった。


さて。ちょっとここで視点を変えてほしい。
「TSCに SC を掛けて、その後 1024 で割ると nano sec になる」
って事は、

TSCにSCを掛けると
大雑把に pico sec の精度の数字ができる

って事だ。
210 = 1024 ≒ 1000 = 103

なので、メートル法の縮尺単位が1つ変化する度に10bit消費される。
(ここは試験に出ます)

すると、pico sec レベルでの数字を扱うと、1秒を扱うには 40bit 必要、ということだ。しかるに、この値は 64bit の変数に代入されいてる。って事は「1秒以上」の表現には 24bit しか有効桁数がないって事だ。

224 / ( 24 * 60 * 60 ) = 194.180741日

実際には、1024の方が若干大きいので、その端数が載る。具体的には 64bit変数を最後に 10bit 右にシフトするので、有効桁数は 54bitになる。これが nano sec で表された時刻なのだから、

254 / ( 24 * 60 * 60 * 1000 * 1000 * 1000 ) 
= 208.499983日

めでたく、今回 overflow を起こした日数そのものが出てくる。64bitは、無限にでかいに等しい桁数ではないのだ。

かくして、cycles_2_ns() は、208.5日経つと、桁あふれを起こして 0 に近い値を出力する。sched_clock() はこの値を利用しているため、ここもぶち飛ぶ。

スケジューラーはここの値に強く依存しているので、突如として 54bit の数字が 0 になると、カーネルのあちこちがぶっ飛んでしまい、reboot に至る。


感想
497日問題の反省
全く活かされておらずっっ!!!

2011年12月11日

lunar eclipse

0 コメント
On year 2011, Dec. 10th around 21:30 - 00:00, we had Lunar Eclipse here at Japan.

Here's some of the photos I took.

Camera: RICOH CX1
Mode: ... ah .. I kept on changing it in the dark. So I'm not sure.
























And .. Battery run out ..... gosh ...

2011年11月24日

ホームゲート

0 コメント
都営大江戸線の青山一丁目駅。

ホームゲートが設置されているのに、まだ稼動していない。
ゲートは全開でその先にレールが見えている。

実運用が始まるとこんな光景はもう見られない。珍しいので撮影。

2011年9月23日

< 2.6.17 な Linux に存在する flock の double free バグ

1 コメント
Linux Kernel 2.6.17(正しくは 2.6.16.x で入ったのかもしれないが…) で新しく入った修正の一つに、flock(2) システムコール起因でカーネルがメモリをダブルバインドする、というものがある。

これを修正するパッチ自体は、2つ。
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=993dfa8776308dcfd311cf77a3bbed4aa11e9868
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=9cedc194a7735e5d74ad26d3825247dc65a4d98e
最初のパッチが問題の本質を直して、2つ目のパッチはその修正の際に、異常系処理が甘くてエラーが還らなくなっていたのを直した、というものだ。

あぁ、あれね、と判っている人はこのバグが高負荷時に結構酷い確率で頻発することも知っているでしょう。
RHEL4U4でもまだ直っていないので、是非 RedHat にギャーギャー言ってください。
*1

当然、ここでは「で? それはどういうバグなの?」という質問してくれる、ヨイ子のみんなのためにある。つーてもいきなり答はタイトルに書いてありますが。


*1) morikawaさん、情報ありがとうございます _o_。
つーかこれは修正し忘れですね。大昔、これを書き始めた時の情報を更新し忘れてました。



なぜコレをここに書くのかと言うと、理由は2つ。

1つ目は Linux Kernel の内部構造が double free に弱い(コレ自体はしょうがない)ため、類似のバグがあると、このバグを踏んだかなり後になってから いきなり kernel がぶっ倒れる、という事が多々あるからだ。この症状から真の原因を直接求めるのははっきり言って不可能に近い。が、なぜ不可能なのかをよく判っていない人に説明するのもかなり困難だ。なのでここに書いておけば、再びそういう目にあったときもここを見れば判る。

2つ目は、このバグがタイミング依存性の高い、かなりいやらしい構造をしたバグだから、だ。同期系のプログラムはかなり丁寧に動作を追わないと、いつ足元を掬われるか判らない。そのためには、いろいろな「実在したバグのケース」を記録しておくのが一番だ。というか、ややこしすぎて厳密に覚えていないと意味がないのに、すぐ忘れるからここに書くわけ。


とりあえず、パッチは上記にあるが、ソースコードはこんな感じだ。

2.6.16: http://lxr.linux.no/linux+v2.6.16/fs/locks.c の flock_lock_file()
2.6.17: http://lxr.linux.no/linux+v2.6.17/fs/locks.c の flock_lock_file()

で、この flock_lock_file() を呼び出しているのは sys_flock() 関数… flock() システムコールの入り口だ:
2.6.16: http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L1495
2.6.17: http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1535

sys_flock() の中で flock_lock_file_wait() という関数が呼ばれている:
     http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1569

flock_lock_file_wait() はここだ:
     http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1496
で、flock_lock_file_wait() の中のここで flock_lock_file() が呼ばれている:
     http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L1501

これが、全体の流れだ。

で、メインの処理は flock_lock_file() が受け持っている。

まず、flock_lock_file() の中で kernel 全体を lock している。つーてもシングルプロセッサの場合は何もしていなくて、SMPの場合だけ、lock が必要な場所で lock をかけている。これが lock_kernel() と unlock_kernel()。もし、kernel_lock がすでに取られていたら、SMPの場合はこのロックが解除されるのを待つことになる。

先に 2.6.17 …つまりバグが修正された後のコードを見て欲しい。lock_kernel() が呼ばれているのはここだ:
        http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L739
一方、unlock_kernel() が呼ばれているのはここ:
        http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L788

ようするに入り口の所すぐで kernel_lock を取得し、この関数から出ていくまで一度も解放しない。これが正しいやり方だ。この間のどこかで kernel_lock を解放してしまうと、仮にそのあとすぐ kernel_lock を再取得したとしても、その間に別CPUが処理に入り込んでくる危険性がある。

で、一方で 2.6.16 は
2.6.17と同様、関数の入り口でkernel_lock を取得している。
         http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L722
で、関数の出口で kernel_lock を解放している:
         http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L768

が、その途中で一度 kernel_lock を解放し:
        http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L737
再度取得し直している。
        http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L749

問題は、この L737 出の開放の後、L749 での再ロックまでの間に、別のCPUが flock_lock_file() 関数に入ってくる危険性がある、ということだ。最初の git パッチ にはこうある:

sys_flock() currently has a race which can result in a double free in the
multi-thread case.

Thread 1 Thread 2

sys_flock(file, LOCK_EX)
sys_flock(file, LOCK_UN)

If Thread 2 removes the lock from inode->i_lock before Thread 1 tests for
list_empty(&lock->fl_link) at the end of sys_flock, then both threads will
end up calling locks_free_lock for the same lock.
ちょっと見辛いし判りづらいな:
SMP環境において

Thread1: sys_flock(file, LOCK_EX)
Thread2: sys_flock(file, LOCK_UN)

がほぼ同時に呼ばれたとする。ただし、最初に kernel_lock を取得したのは Thread1 の方だ。で、Thread1 が最初の unlock_kernel() を呼んだ。

この瞬間に Thread2 は kernel_lock を取得できる。で、ここ:
        http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L734
この場所で、inode->i_lock を解放する。
で、ここを通り抜けた後、Thread2 が unlock_kernel() を呼ぶと、Thread1 が flock_lock_file() の後半部を通り抜け、flock_lock_file_wait() へと戻り、そこから sys_flock() へと帰る。

問題は、Thread1 が sys_flock() へと帰った後。ここ:
http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L1532
で、lock_free_lock() を呼んでしまうことだ。結果として、同じロックを2重解放してしまう。

lock_free_lock() のコードはここだ:
2.6.16:    http://lxr.linux.no/linux+v2.6.16/fs/locks.c#L157
2.6.17:    http://lxr.linux.no/linux+v2.6.17/fs/locks.c#L169

ここで問題なのはただ1点、kmem_cache_free() を呼び出すところだ。これを同じ構造体に対して行なってしまう。結果として、2重解放が行われる。





で、これはどういうバグなのか? というのがここで書くお話。
なのだが、まだ書いてない(酷いっ)。でもとりあえず、そういうのがあるよ、と言うところまで公開しておく。
# git serverが復活したっぽいのと、lxr 上で説明したほうがある程度判りやすいので、
# その両方の準備をする。

2011年9月16日

Brave to stay

0 コメント
This brave Mantis has been on "outside" of the window of 29th Floor elevator hall since noon.
I shoot this photo on my way back to home, 17:48, and (s)he was still there!

Hanging upside down on the glass must be tremendously tough job. And since (s)he have a wing to fly, it's not hard for h(im|er) to give up and fly a little bit aside for better environment. What a brave.

...

Well ... it might simply be that this insect was stupid as to stay though ... staying on tough situation for nothing ...

2011年6月11日

リサイクルループに着目しよう

0 コメント
節電が叫ばれている。一般家庭も企業も15%ぐらいは節電して欲しいそうだ。

で、老人が「ペカペカ光るもの」は「電気を食うに違いない」とボケを垂れ流している。が、正直、それではたいして節電にならない。本当に電気を食うものってそんな所にはないからだ。

というか、「ペカペカと光る」とか「キンキンに冷える」とか、そういう擬音付きキーワードな代物は、別に昨日今日目の敵にされたわけじゃない。なのでアチラコチラに工夫がしてあって、実はかなり電力を食わないようになっているのだ。だからそんな所を今更攻めたって、たいして節電になどならぬ。

じゃぁ、どこにエネルギーは消費されているか?
エコロジーだともてはやされたもの。CO2削減にはこれ、ともてはやされたものにこそエネルギー消費は隠れている。
というわけでリサイクルを槍玉にあげてみよう。

リサイクルって言うのは「モノ」の形状を変えて、同じ物体をなんども変形させながら使い回しする、っていうのが基本発想だ。ペットボトルを粉砕して溶かしてもう一度ペットボトルにするとか。紙をパルプに戻して漂白してまた紙にするとか。「モノ」を無駄に破棄しない、と言う意味では素晴らしい。

でもちょっとまってくれ。そもそも、その「モノ」は何のために消費されているのだ? 我々は「ペットボトルを買いたい」わけじゃない。我々は「紙が欲しい」わけじゃない。
ペットボトルなら、その中にあるジュースが欲しいのだ。紙が欲しいのではなく、紙に印刷されている情報が欲しいのだったり、紙に包まれている中身が欲しかったりするのだ。まさに輸送のためのケースがペットボトルであり、紙なわけだ。だからこそリサイクル出来るぐらい損傷少なくペットボトルや紙を回収できる。

しかし、リサイクル・ループはエネルギー的に見れば決してただじゃない。輸送にはガソリンが、加工には電気が使われているはずだ。

じゃぁ、ペットボトルを使わない、紙を使わないで目的を達することは出来ないだろうか?
非炭酸系の飲み物をペットボトルから紙容器に切り替えるとか。
新聞を紙から電子版に変えるとか。

本来の目的からすれば別に欲しいわけじゃないモノを使わないようにする事で、リサイクル・ループを流れるモノの絶対量を減らす。しかも「本来の目的」は別にセーブしなくてよい事になる。


今回の節電は、目先の電力消費量を変えるのではなく、リサイクル・ループを流れる物流を減らしつつ、無駄にモノを廃棄しない、という形を取らないと苦しいと思う。目先のペカペカしたものを排除するような発想では、長期的に見ると必要なエネルギーが減らないままだ。

2010年12月5日

【DS】A列車で行こうDS 「青葉経済開発区」「標準」

0 コメント


というわけで、新しい地図「青葉経済開発区」…と言っても、演習問題でやった最後の地図だが。これの「標準」モード。

目標は複数。

  • 総資産1000億円
  • 2年連続黒字
  • 子会社数10社
以上を10年以内で。

とはいうものの、これだけだと簡単だ。そこで追加の条件。

  • 株式公開しない
  • 借金しない
  • 地下鉄駅は作らない(陸橋上に駅は作るし、一部は地下鉄にする)
  • 鉱脈は掘らない
  • 資材置き場は全部地面の下(だって地上に用意するのもったいないじゃん)
最初の資本金は600億円で、この一部が土地だの電車だのになった状態でスタートする。2年連続で黒字になれば、株式公開できる。すると資産が一気に増える。ここに借金分を追加すれば総資産1000億円は実は簡単だ。子会社だって黒字・赤字を無視すれば10社ぐらい簡単に作れる。運がよければ黒字になって、総資産を増やしてくれるだろう。

が、株式公開も借金もしないとなると、一気に条件が難しくなる…はず。


この土地は丘の上と下に別に町があるので、それぞれについて小さなループを作ってやる。とりあえず、一方づつ片付けつつ、もう一方もある程度発展してもらうには、この小さなループが一番。ただし、あとでループを伸ばせるように、伸張部分相当の土地は買っておく。

丘の上のループと丘の下のループそれぞれ上下の水平線路の間隔が10マスになるように取る。すると駅をうまく配置すると、町が発展したときに複数の駅が同時に発展する事ができる。並列な線路それぞれの駅の影響範囲をオーバーラップさせればいいわけだ。で、それを丘の上と下でも実装するために、丘の上と下の間、丁度10マスの所に水平に土地を買っておく。

で、とりあえず子会社を10個作って稼がなくちゃいけない。そこで上の範囲指定。離れ半島の根元、一番左側だ。
ここに工場地帯を作る。目的は画面外の「隣町」への資材の輸出。
一番最初は線路を引いて、隣町から資材を輸入。資材が1つでも発生したら、速攻で工場を建て始める。で、工場ができたらこれまた速攻で輸出に切り替える。これで期限内に黒字になるに違いない…と思ってやってみたのだが…
実際には、工場を建てるコストが結構高いので、10年以内に黒字になりませんでした。つまり計画倒れ。

もう一つ、打った対策がこれ。上のループ側に工場があるように見えなかったので、下の工場から道路を引っ張って上のループのそばに1つバス停を設置、その真下に資材置き場をたくさん用意して、資材不足にならないようにした。

というわけで、以上の敷設が終わった直後の地図がこれ。
線路とか、ほとんどまだ撤去できていません。撤去費用がもったいないんだもの。その間に地下鉄の技術を開発する。

さて。しばらくして人口が増えてきたら、路線を延ばします。伸ばし方はこう:
下のループを解除してうねうねと南の端まで。南の端で線路が切れてる?!いいえ。では見てみましょう。
見てのとおり、南の端は地下に潜っています。
で、出てくる側はここ。とりあえず、地下を通って一気に北上します。

さらに時間が過ぎて、人口が増えてきたら、さらに鉄道を延ばします。こんな感じ:
この段階で、南の端から潜った電車は、画面右端の一番北側…右の方に「山」があるんですがその根元から湧き出しています。B1だとまっすぐ伸ばすのが難しいのでB2まで沈んで、一気に北東へ、で必要な高さまで再び上昇。

さて。会社運営は初年度を除いて毎年ギリギリ黒字でして。ですので2年連続黒字は簡単。子会社も工場を建てたり、マンションAを建てたので10社は楽勝。唯一の問題は資産1000億円なんですが。

10年目の8月。ギリギリ、なんとか資産1000億円クリア。
見ての通り、どうにか全部あわせて月20億円づつ収益が上るようになったので、それを子会社に再投資…を続ける。あ、あと福利厚生は毎年ガッチリプランに入れて、社員のやる気を高めます。
1月から5月までは一切投資せず、6月から12月に一気に子会社を作ると、だいたい収入の40%を税金用にプールできるので。8月だから残り7ヶ月ですか。結構ヤバかったかも。

さて、条件をクリアしたら、あとはひたすら発展させていくのみ。

見ての通り、10マスごとに東西に平行な線路を引いていくわけですが、一気に伸ばすと人口密度が薄くなってしまうので、とりあえずは上下方向に伸ばすようにしました。左右方向はとりあえず上下方向が一通りそろったら。

で、左右方向の左端から半島に、半島から南の島へと線路を延ばそうかと。あ、半島の右にある島は開発対象にしません。面積が狭すぎて駅が建てられねぇ。

で、この辺まで伸ばすと、資材不足が頻発します。工場から遠すぎて、資材を必要とする場所と、工場との間に別の資材不足が発生すると、資材が届かなくなる。そこでカーソルのある場所にも工場を建てることにしました。
もちろん、画面ギリギリに作っているのは、これらの工場群の第一目標はあくまでも隣町への資材の輸出だから。資材が必要ないときは輸出量を多く、必要になったら少なく調整する。

というか、これは発展させていく過程で判ったのですが、巨大な1つの循環路は、線路を延ばすたびに貨物列車が一周する時間が長くなります。で、そうすると工場から資材を回収する頻度が下がり、輸出量を多くしなくちゃいけない。でも我々の町側も実はあちこちで資材を必要としていて、貨物列車の数が足りていない(資材不足が発生しまくる)という問題が…。


そうこうしているうちに、新たなる問題が。
判りますか? 駅が建設できないんです。
…さっっ、先に言ってくれぇ(T.T)
って言うか、少なすぎるだろう。80って。

とりあえず、駅を増やせない以上、どこかの駅を引っぺがして新しく配置するしかない。
幸か不幸か、今の駅の配置はほぼ全ての土地を2つ以上の駅が共有するようになっている。なので、どこか…そう。南側の土地を集中的に開発してしまい、その場所については急激な人口増加を必要としない状態にしてしまえば駅密度は半分…つまり土地の発展に影響される駅の数が1つになるようにできる。に違いない。
で、とりあえず、駅を引っぺがせるようになるまでの間に、残りの線路を引いておく。

半島に入る線路は、北側が半島に入るほう、南側が半島から出る。

半島に入ったらまず、最初に作った工場のそばを通り、ここで資材を拾う。で、その直後に地下に潜って、島の北側へ。島をグネグネと移動したら、今度は半島の南端へ橋を渡る。で今度はグネグネと半島を北上する。海岸が東西方向に近い部分は一部の引き戻しを地下で行うけれど基本は地上を通るように線路を引く。で、最後に半島を抜けて、大陸側のループに戻る。

ここまでで61年目。
なのだが、ここからが長かった…

結局ループが長すぎて、地図全体で4箇所も工場地帯を作っているのに、資材配給がどんどん滞るようになっていく。とりあえず、全体開発完了の目標として、総人口100万人を目指したのだが、資材が届かないのではいかんとも発展しがたい。当然子会社の上限とかが先に来るわけで…作っては売り、を繰り返すと社員のやる気がすごい勢いで落ちていくし…。
結局、我慢比べ大会のような状態でジリジリと開発を進めていって、総人口が100万人を超えたのは 190年後の事に。全体はこんな分布:

会社の状態はこんな感じ:
やはり、もう社員の不満はどうしようもなく。

会社沿革はこう:

なによりも残念なのは、人口を100万人にしても会社沿革に載らない事。いや、総資産10兆円オーバーも、100兆円オーバーも載らないんですけどね。

結局地図全体はこうなりました:

最終的な反省点。
  • 巨大循環路は退屈だ。
    なにしろ時間を掛ければ絶対成長する事が判りきっている。一方で、資材不足とかが頻発しすぎる。循環路自体はともかく、全部を1つの循環路にするのは考え直したほうがよさそうだ。
  • 隣町との交易専用の駅は、駅舎の無駄。
    隣町へ輸出するために工場のそばに駅を作るのはいいのだが、距離を最適化しすぎると、輸出にしか使えない駅になってしまう。結果として、その工場群から「自町用の資材」を拾うための駅をもう1つ作る羽目になり、これが駅の無駄遣いになる。
    1つの駅で両方の目的をクリアし、ついでに町の発展分(21x21マス)の計算にも入れられるよう、配置するべき
というわけで。以上の反省点を踏まえて、「いばらの道」を攻略する事にしよう。