Commit graph

4401 commits

Author SHA1 Message Date
ea94ec2f18 Disable damage to base when missile explodes on launch
When a missile explodes on launch, it has a 33% chance to damage its
base.

Unfortunately, damaging the base breaks callers that call msl_launch()
for each member of a list of missiles created by msl_sel() or
perform_mission().  Damage to the base can damage other missiles
there.  Any copies of them in the list become stale.  When
msl_launch() modifies and writes back such a stale copy, the damage
gets wiped out, triggering a seqno oops.

Affects missile interdiction and interception using missiles with
non-zero load.  Stock game's ABMs have zero load, so interception is
safe there.  Relatively harmless in practice.  Broken in Empire 2.

Instead of fixing the bug, simply disable damage to the base for now.
2012-05-22 20:56:47 +02:00
0dd59211aa Fix march not to wipe out concurrent updates
March code reads the land units into a land unit list, and writes them
back when it changes them, e.g. when a land unit stops.  If a land
unit changes in the land unit file while it is in such a land unit
list, the copy in the land unit list becomes stale, and must not be
used.

To that end, do_unit_move() calls lnd_mar() after prompting for path
or destination.  lnd_mar() re-reads all the land units.
Unfortunately, it still writes back stale copies in certain
circumstances.  Known ways to trigger such writes:

* Deity loads land unit onto a ship or land unit

* Land unit's crew killed just right, e.g. by collateral damage from
  interdiction, followed by additional updates, such as shell fire
  damage

* Sector no longer owned or allied, e.g. allied sector captured by an
  enemy (own sector would kill or retreat the land unit)

Writing a stale copy wipes out the updates that made the copy stale,
and triggers a seqno mismatch oops.  For instance, damage that follows
killing of all crew by collateral damage from interdiction is wiped
out.  If no damage follows, we still get a generation oops.
2012-05-22 20:38:19 +02:00
a694e49343 Fix navigate not to wipe out concurrent updates
Navigation code reads the ships into a ship list, and writes them back
when it changes them, e.g. when a ship stops.  If a ship changes in
the ship file while it is in such a ship list, the copy in the ship
list becomes stale, and must not be used.

To that end, do_unit_move() calls shp_nav() after prompting for path
or destination.  shp_nav() re-reads all the ships.  Unfortunately, it
still writes back stale copies in certain circumstances.  Known ways
to trigger such writes:

* Deity sets a sail path

* Ship's crew gone, e.g. killed by shell fire

* Sector no longer navigable, e.g. harbor shelled down, or bridge
  built

Writing a stale copy wipes out the updates that made the copy stale,
and triggers a seqno mismatch oops.  For instance, ship damage that
kills all crew while the ship is being navigated gets wiped out.
2012-05-22 20:38:19 +02:00
Ron Koenderink
0700d97fe3 Fix Windows build: gettimeofday() and SHUT_WR missing
Commit 904822e3 introduced use of SHUT_WR, which Windows calls
SD_SEND.  Add the obvious work-around.

Commit 49ae6a7b introduced use of gettimeofday(), which the Microsoft
CRT lacks.  Add a replacement based on _ftime_s().
2012-05-22 20:38:10 +02:00
6c5dfc870e Update change log again for 4.3.30 2012-05-05 16:18:14 +02:00
4801fad4a8 Fix buffer overruns in fairland for island size zero
Fairland creates islands with size 1 + random() % (2 * is - 1), where
"is" is either chosen by the user (fourth command line argument) or
defaults to half the continent size (second command line argument).
Negative values are silently replaced by zero.

Not only does value zero make no sense, it also breaks the code: the
island size is always one then (because random() % -1 is zero), but
allocate_memory() provides only space for zero sectors in sectx[],
secty[] and sectc[].  This leads to buffer overruns in try_to_grow(),
find_coast(), elevate_land, set_coastal_flags().  Can smash the heap.

Fix by changing the lower bound from zero to one.  Diagnosed with
valgrind.  Has always been broken.
2012-05-05 16:13:08 +02:00
3464a4a9d0 Fix an out-of-bounds subscript in fairland
elevate_land() tests for capital sector in three places.  The third
one is broken: half of the test is done even for islands, subscripting
capx[] and possibly capy[] out of bounds.  This could screw up
elevation (unlikely) or crash (even less likely).  Diagnosed with
valgrind.

Broken since the test was added in Chainsaw 3.12.  Parenthesis were
added blindly 4.0.11 to shut up the compiler.  Reindentation (commit
9b7adfbe and ef383c06, v4.2.13) made the bug stand out more, but it
still managed to hide in the general ugliness of fairland's code.
2012-05-05 13:46:15 +02:00
b4b38bf859 Fix typo in change log 2012-05-05 09:17:00 +02:00
fe33d92ed0 Update change log again for 4.3.30 2012-05-01 18:39:35 +02:00
540526a140 Start the makefile's dependency section with a comment
Just to separate it visually from the preceding section
2012-05-01 18:37:50 +02:00
5729c18458 Journal login before changing the player thread's name
The journal logs a thread name for each event.  The player thread name
changes on entry to the playing phase.  Connecting old and new name
isn't as easy as it should be:

    Sun Apr 29 12:13:39 2012     Conn29 input coun POGO
    Sun Apr 29 12:13:39 2012     Conn29 input pass peter
    Sun Apr 29 12:13:39 2012     Conn29 input play
    Sun Apr 29 12:13:39 2012     Play#0 login 0 127.0.0.1 armbru
    Sun Apr 29 12:15:39 2012     Play#0 logout 0

To connect Conn29 with Play#0, you have to know that country#0 is
named POGO.

Fix that by logging login before the thread name change:

    Sun Apr 29 12:17:41 2012     Conn29 input coun POGO
    Sun Apr 29 12:17:41 2012     Conn29 input pass peter
    Sun Apr 29 12:17:41 2012     Conn29 input play
    Sun Apr 29 12:17:41 2012     Conn29 login 0 127.0.0.1 armbru
    Sun Apr 29 12:19:41 2012     Play#0 logout 0

Now "Conn29 login 0" makes the connection obvious.

This involves moving journal_login() from player_main() before
empth_set_name() in its caller play_cmd().  Move journal_logout() as
well, for symmetry.

If player_main() fails, we now log login/logout instead of nothing in
the journal.  That's okay.  Note that before commit c9f21c0e (v4.3.8),
we logged just login then.
2012-05-01 18:37:49 +02:00
eed7a46aed Fix arm to require nuke and plane to be in the same sector
It happily arms a plane with a remote nuke.  The nuke gets teleported
to the plane when the plane moves (a two-way sortie doesn't count as
move).  Broken in 4.3.3.  Reported by Harald Katzer.
2012-05-01 18:35:32 +02:00
4aaea42a83 Update change log for 4.3.30 2012-04-26 20:15:48 +02:00
0b1218f164 Document login_grace_time and the shutdown phase properly 2012-04-26 20:08:58 +02:00
bc629096c0 Don't send "idle connection terminated" in login phase
Message was introduced in commit 08b94556, v4.3.20.  Revert this
change, because it's undocumented, and probably not useful for
clients.
2012-04-26 20:08:57 +02:00
2503e41bd2 Rename play_lock back to update_lock
It was renamed to play_lock because it synchronized not just updates
but also shutdown.  Since the previous commit, it again only
synchronizes updates.  Rename it back.

Also move its initialization next to shutdown_lock's.
2012-04-26 20:08:57 +02:00
49ae6a7b9d Fix synchronization between shutdown and player threads
shutdwn() sets the EOF indicator, aborts the running command, if any,
forbids sleeping on I/O and wakes up the player thread, for all player
threads in state PS_PLAYING.  It takes play_lock to prevent new
commands from running.  It then waits up to 3s for player threads to
terminate, by polling player_next(), to let output buffers drain.

Issues:

1. Polling is lame.

2. New player threads can still enter state PS_PLAYING.  They'll block
   as soon as they try to run a command.  Somehwat unclean.

3. We can exit before all player threads left state PS_PLAYING, losing
   a treasury update, play time update, and log entries.  Could happen
   when player threads blocked on output until commit 90b3abc5 fixed
   that; its commit message describes the bug's impact in more detail.
   Since then, the bug shouldn't bite in practice, because player
   threads should leave state PS_PLAYING quickly.

Fix by introducing shutdown_lock: player threads in state PS_PLAYING
hold it shared, shutdwn() takes it exclusive, instead of play_lock.
Takes care of the issues as follows:

3. shutdwn() waits until all player threads left state PS_PLAYING, no
   matter how long it takes them.

2. New player threads block before entering state PS_PLAYING.

1. shutdwn() still polls up to 3s for player threads to terminate.
   Still lame.  Left for another day.
2012-04-26 20:05:28 +02:00
86f0294573 Start player thread shutdown grace time at shutdwn() entry
Before, it was started after all commands aborted.  Shouldn't make a
difference in practice, as command abortion is supposed to be quick.
2012-04-26 19:57:19 +02:00
786e2a99d5 Clean up superfluous includes 2012-04-26 19:57:19 +02:00
f75c03dfd9 Belatedly update convert's c_form
Commit 82c91665 (v4.3.16) removed its optional third argument without
updating c_form.
2012-04-26 19:57:19 +02:00
02293b9bd1 Document the header for empmod.c and trdsub.c in prototypes.h 2012-04-26 19:57:19 +02:00
f802630fa3 Document execute()'s subtle use of player->aborted 2012-04-26 19:57:19 +02:00
a1003ea7da io_shutdown() is now unused, remove 2012-04-26 19:57:19 +02:00
eb25be08d4 Change login command kill to kill less ruthlessly
The victim's connection closes without any explanation.  Output may be
lost.  This is because kill_cmd() kills by calling io_shutdown(),
which shuts down the socket and drains the I/O queues.

How this makes the victim's thread terminate is a bit subtle: shutting
down the socket makes it ready.  If the victim's thread is waiting for
I/O, it wakes up.  Since all further reads return EOF, and all further
writes fail, the command terminates quickly (short of inifinite loop
bugs), then the command loop, and finally the thread.

To make kill behave more nicely, change kill_cmd() to work exactly
like server shutdown: send a flash message to the victim, set his EOF
indicator, abort the command, forbid sleeping on I/O, wake up the
victim's thread.  Just as reliable, but doesn't lose output.

If the victim's client fails to close his connection, the victim's
thread may still linger in state PS_SHUTDOWN for up to
login_grace_time (default 120s).  An attacker could try to use that to
make the server run out of file descriptors or memory, but simply
connecting achieves the same effect more cheaply.
2012-04-26 19:57:19 +02:00
918f3ec6ae Separate max_idle_visitor from max_idle
Cut it to 5 minutes, from max_idle's 15.

Since max_idle now applies only to authenticated players, increasing
it is perfectly safe.
2012-04-26 19:57:19 +02:00
1a97cc3cfd Separate login_grace_time from max_idle
max_idle applies in state PS_PLAYING, login_grace_time before (login,
state PS_INIT) and after (logout, state PS_SHUTDOWN).

Cut login_grace_time to two minutes, from max_idle's 15.  Two minutes
is plenty to complete login and logout.  Makes swamping the server
with connections slightly harder, as they get dropped faster.  While
that makes sense all by itself, the real aim is making increasing
max_idle safe.  The next commit will complete that job.
2012-04-26 19:57:19 +02:00
fb9595fe6a Fix unwanted player thread blocking on output during shutdown
shutdwn() disables blocking on I/O for all player threads in state
PS_PLAYING, by setting struct player member may_sleep to
PLAYER_SLEEP_NEVER.  This ensures the player threads complete logout
quickly and reliably.  A thread may still block on I/O in io_close()
called from player_delete(), since commit 904822e3, but that's okay,
because it happens after all game state updates.

Bug: if shutdwn() aborts a command, the player thread returns through
dispatch(), which resets may_sleep back to PLAYER_SLEEP_FREELY.  Input
can't block regardless, because the EOF indicator is set, but output
can.  When it happens, the player thread may not complete logout
before shutdwn() terminates the process.

This can make us lose a treasury update (similar to the bug fixed by
commit bdc1c40f; the relevant bug description is in commit note
6f8ca87f), play time update, and log entries.

How?  There are two paths from dispatch() to player_delete().  Here's
the first one:

  1. command()
     Doesn't print since dispatch() returns 0 when it resets may_sleep
  2. player_main()
     Loop and call status()
  3. status()
     If the command set dolcost to a non-trivial amount, print it
     Charge dolcost
     If player went broke or became solvent, notify him
     Charge time used
     Return 0, because shutdwn() set the EOF indicator
  4. player_main()
     Break the loop
     Charge time used
     print Bye-bye
     journal.log the logout
  5. play_cmd()
     server.log the logout
  6. player_login()
     Loop
     Try to flush output
     get EOF, break loop
     print so long
     call player_delete()

Ways the bug can bite:

A. When we block in 4. print Bye-bye, we can fail to log.

B. When we block in 3. print broke/solvent notification, we can
   additionally fail to charge time used.

C. When we block in 3. print dolcost, we can additionally fail to
   charge dolcost.

Note: B. and C. couldn't happen before commit bdc1c40f.  Instead,
something just like C happened always, whether player thread blocked
or not.

The second path:

  1. execute()
     Loop and call status()
  2. status()
     As above
  3. execute()
     break the loop
  4. dispatch()
     Continue with the first path

No additional ways to bite.

Fix by avoiding the may_sleep reset when the player thread is on its
way to terminate: may not sleep and has its EOF indicator set.

Broken in commit 0a4d77e9, v4.3.23.
2012-04-26 19:57:14 +02:00
cac506f664 Fix pr_player() and upr_player() to obey max_idle
The output queue flush can block indefinitely.  Permits a client to
hog the thread indefinitely by not reading output.

Broken in commit 08b94556 (v4.3.20) "Reimplement max_idle without a
separate thread".  Until then, the idle thread aborted a stuck attempt
to flush output.

Denial of service seems possible.
2012-04-26 19:43:42 +02:00
7980465199 Factor player_output_some() out of pr_player(), upr_player() 2012-04-26 19:43:42 +02:00
4d59881505 Fix recvclient() to obey max_idle for output, too
recvclient() flushes the output queue before receiving input.  The
receive obeys max_idle, the flush doesn't.

Broken in commit 08b94556 (v4.3.20) "Reimplement max_idle without a
separate thread".  Until then, the idle thread aborted a stuck attempt
to flush output.
2012-04-26 19:43:42 +02:00
7cca82578d Clean up how recvclient() deals with command abortion
We must not block in io_input() after command abortion unblocked
io_output().  Instead of checking player->aborted, compute the
deadline according to player->may_sleep, like we do for io_output().
2012-04-26 19:43:42 +02:00
1dd44b7eea Fix player_login() to obey max_idle for output, too
player_login() flushes the output queue before receiving input.  The
receive obeys max_idle, the flush doesn't.  Which means a client could
hog the thread indefinitely.

Broken in commit 08b94556 (v4.3.20) "Reimplement max_idle without a
separate thread".  Until then, the idle thread aborted a stuck attempt
to flush output.

Denial of service seems possible.
2012-04-26 19:43:41 +02:00
d381351c65 Flush all output before reading a login command, not just some
Before, a client could theoretically make the output queue grow
without bounds.
2012-04-26 19:43:41 +02:00
5ce099f671 Fix io_close() to obey deadline for output, too
A client can delay thread exit indefinitely by not reading output.
Broken in commit 08b94556 (v4.3.20) "Reimplement max_idle without a
separate thread".  Until then, the idle thread aborted a stuck attempt
to flush output.

Denial of service seems possible.

Note that commit 904822e3 moved flushing the output queue from
player_login() to io_close().  It also made io_close() wait for the
client to close the connection.  That wait obeys the deadline.
2012-04-26 19:43:41 +02:00
7a3cbf037a Add deadline support to io_output(), io_output_if_queue_long()
Replace parameter wait by deadline.  Non-zero wait argument becomes
(time_t)-1 argument, zero wait argument becomes zero deadline
argument.  No functional change, yet.
2012-04-26 19:43:22 +02:00
ddbcce12c3 Switch io_close(), io_input() from timeouts to deadlines
All users want deadlines.  Move the conversion from deadline to
timeout from callers to io.c, where it becoems an implementation
detail.
2012-03-31 19:03:19 +02:00
10768189e2 Unbreak nightly build
The nightly build uses a gross hack to keep timestamps stable: it
replaces the system's time() by emp_time().  It doesn't replace other
time-related functions such as gettimeofday().  Works as long as we
don't mix hacked time with unhacked time.

The previous commit compares time gotten from gettimeofday() with time
gotten from time().  The nightly build's I/O timeouts become zero,
which makes login impossible.

Replace gettimeofday(), too.
2012-03-31 19:03:19 +02:00
ed1cbc97e6 Base idle timeout on player->curup again, not current time
Idle timeout used to expire max_idle minutes after the last
player->curup update.  When we got rid of the idle thread in commit
08b94556 (v4.3.20), this got changed to "wait no more than max_idle
minutes for input".  Time spent computing and time spent blocked on
output no longer counts.  In particular, a connection can block
indefinitely on output since then.  Let's fix that.

Start with basing the input timeout on player->curup again.  The
missing output timeout will be added shortly.

Aside: since status() updates player->curup, the idle timer gets reset
when the update aborts a command.  Left for another day.
2012-03-27 17:23:18 +02:00
a96b400da3 Replace the per-iop input_timeout by per-function timeouts
Commit 08b94556 (v4.3.20) added io_open() parameter input_timeout.  It
applies to io_input() and, since commit 904822e3, to io_close().  Add
timeout parameters to these functions instead.
2012-03-27 17:23:14 +02:00
0513ec136b Clean up io_input() for the "no input available" case
Return zero when no input is available, regardless of parameter
waitforinput.  Before, it returned -1 with errno set to EAGAIN or
EWOULDBLOCK when not waiting for input.  Current callers all wait.

Drop errno from the function's contract, for consistency with
io_output().
2012-03-27 10:13:05 +02:00
5501efa278 Fix buffer overflow in build
Remote hole, can smash the stack.  Broken when Empire 2 added the
confirmation prompt.
2012-03-27 10:13:05 +02:00
6c927dc3c3 Fix buffer overflow in scrap and scuttle
Remote hole, can smash the stack.  Additionally, the confirmation
prompt is misleading when the player supplies conditionals.  Redesign
the flawed prompt.

Broken when Chainsaw added the confirmation prompt.  Reported by Scott
C. Zielinski.
2012-03-27 10:12:37 +02:00
8b7c78b5fa Fix execute's printing of the executed commands
If execute's argument was read interactively, it prints the argument
instead of the command.  Else, it prints uninitialized garbage.  The
latter can theoretically crash the server, or leak information.

Broken in commit 3de1e8be, v4.3.28
2012-03-15 21:38:29 +01:00
68dc9b2936 Fix uninitialized variable use in defending land unit retreat
lnd_take_casualty() uses uninitialized rsect to compute the mobility
cost of retreating a defending land unit.  This can charge incorrect
mobility, prevent retreat, or, if the stars align just right, crash
the server when sector_mcost() subscripts dchr[] with it.

Broken in commit 4e7c993a, v4.3.6.  Reported by Scott C. Zielinski.
2012-03-05 12:57:52 +01:00
9b92ac633d Fix how play_cmd() ensures connection close
play_cmd() needs to return in a state that makes player_login() break
the login command loop.

play_cmd() assumes player_main() always returns in such a state:
connection's EOF indicator set.  Unfortunately, the assumption is
wrong.  Fortunately, play_cmd() checks it, oopses and recovers.

player_main() can return with neiter error nor EOF indicator set,
e.g. when the game is down.

player_main() can return with just the error indicator set.  For
instance, when the client dies, io_input() detects ECONNRESET, sets
the error indicator and fails.

Broken in commit 8549efbc.  Fix by setting the EOF indicator silently
(without oopsing) when necessary.
2012-02-25 11:32:02 +01:00
ce64540350 Restore amusing comment on naming of empmod.c
Got lost in 4.2.0.
2012-02-25 11:31:51 +01:00
d4a245e0fd Drop C_MOD flag from commands that "obviously" don't need it
C_MOD prevents print functions from blocking.  The common reason for
wanting non-blocking prints are "read, print, write back" patterns.
There might be other reasons lurking in the code, and that's why
messing with C_MOD is scary.

Nevertheless, drop it from cutoff, headlines, land, lstat, motd,
neweff, payoff, qorder, sorder, and wall.  These commands have had
C_MOD "forever", even though they clearly don't modify game state.
They're all pretty simple.  Similar commands such as census, news,
ship, plane, nuke, sstat, pstat, flash never had C_MOD.
2012-02-25 11:25:25 +01:00
d965ddd79e Normalize command capability order in player_coms[] initializer
No functional change.
2012-02-25 11:25:24 +01:00
9bd8521e74 Revise money and capital command permissions
arm and disarm no longer require money, for consistency with the other
commands to move stuff around.

satellite no longer requires money, for consistency with lookout,
radar, sonar and skywatch.

qorder, sorder, survey and test no longer require a capital, for
consistency with the other commands that report on stuff the player
owns.

start and stop no longer require a capital, for consistency with the
other commands to control production.

fortify now requires a capital, for consistency with mission, morale
and range.
2012-02-25 11:25:24 +01:00
f9fc80e2de Revise nation status command permissions
Create new command capability NONVIS.  Give it to players in any state
except visitors (and STAT_UNUSED, but those must not exist).  This
makes it possible to have commands available to anyone but visitors.

Command change fails when the player is a visitor.  Simply make it
unavailable instead, by requiring NONVIS.

Make read unavailable to visitors, because it's useless: visitors
can't receive telegrams (typed_wu() fails).

Make census, commodity and sinfra unavailable to visitors.  Visitors
don't normally have sectors.

Make map and nmap unavailable to visitors.  Visitors don't have sectors,
so their maps are always empty.

Make them unavailable to new players (between add and newcap) and
players in sanctuary, too.  This is consistent with all the other
commands to examine the environment.  It also prevents people from
trying multiple unbroken countries in a blitz to find the one with the
nicest vicinity.

Make resource available to new players, for consistency with census
and commodity.

Make country, echo and financial available to anyone.
2012-02-25 11:24:32 +01:00