]> git.pond.sub.org Git - empserver/commitdiff
Fix buy not to wipe out concurrent updates
authorMarkus Armbruster <armbru@pond.sub.org>
Sun, 17 Apr 2011 04:44:21 +0000 (06:44 +0200)
committerMarkus Armbruster <armbru@pond.sub.org>
Sun, 17 Apr 2011 15:48:18 +0000 (17:48 +0200)
buy() reads the lot, prompts for input, then writes back the lot,
triggering a generation oops.  Any updates made by other threads while
buy() waits for input are wiped out, triggering a seqno mismatch oops.

Since commodities are taken from the seller when he puts them on the
market, and given to the buyer when the trade executes, the wiped out
lot's seller loses his goods without compensation, the other seller
gets to keep his goods, and the buyer receives their duplicates.

This can be abused by two conspiring countries to duplicate
commodities.  The seller puts them on the market (say 100 gold bars).
The buyer starts a buy command, and waits at its last prompt for the
lot to be replaced.  The seller takes them off the market (possible,
since there's no bid, yet), and sells something else (say one food)
quickly enough to get the same lot number assigned.  The buyer then
completes the buy command.  The seller loses one food, the buyer gains
100 gold bars.

Replaces a partial fix from v4.0.1, which only caught lots gone away,
not lots replaced by new ones.

src/lib/commands/buy.c

index 0e9aaae35dea2f31a047cefd5294dcb7930d6ebb..297a9cc4c9e3c78b993bfda5c6b27b6fe5d48809 100644 (file)
@@ -55,7 +55,6 @@ buy(void)
     struct sctstr sect;
     struct natstr *natp;
     struct comstr comm;
-    struct comstr ncomm;
     struct comstr comt;
     struct trdstr tmpt;
     struct ichrstr *ip;
@@ -127,7 +126,7 @@ buy(void)
        }
     }
     canspend = natp->nat_money - tally;
-    getcomm(o, &comm);
+    check_comm_ok(&comm);
     if (bid * comm.com_amount * buytax > canspend) {
        pr("You have overextended yourself in the market\n");
        pr("You can not bid on the current items at that price.\n");
@@ -161,11 +160,8 @@ buy(void)
        pr("You don't have that much to spend!\n");
        return RET_FAIL;
     }
-    getcomm(o, &ncomm);
-    if (!ncomm.com_owner) {
-       pr("That lot has been taken off the market.\n");
+    if (!check_comm_ok(&comm))
        return RET_FAIL;
-    }
     if (bid > 0.04 + comm.com_price) {
        comm.com_price = bid;
        /* Add five minutes to the time if less than 5 minutes */