From 94d31e829e255da1f592cd64ba51ce19fa247adf Mon Sep 17 00:00:00 2001 From: Georg Pfuetzenreuter Date: Thu, 10 Oct 2024 02:33:42 +0200 Subject: [PATCH] Prevent dice overflow rand.Int() would panic when the max value is <= 0, which happens when big.NewInt() was fed with a too large number. Avoid this by validating the big.NewInt() return beforehand. Add error handling to all callers to both gracefully return to IRC and to log an error message. Signed-off-by: Georg Pfuetzenreuter --- wat/game.go | 110 +++++++++++++++++++++++++++++++++++++++++---------- wat/utils.go | 22 +++++++++++ 2 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 wat/utils.go diff --git a/wat/game.go b/wat/game.go index 8de6991..11cf75d 100644 --- a/wat/game.go +++ b/wat/game.go @@ -2,6 +2,7 @@ package wat import ( "crypto/rand" + "errors" "fmt" "math/big" "strconv" @@ -121,9 +122,17 @@ func (g *WatGame) help() string { return ret } -func (g *WatGame) RandInt(max int64) uint64 { - i, _ := rand.Int(rand.Reader, big.NewInt(max)) - return i.Uint64() +func (g *WatGame) RandInt(max int64) (uint64, error) { + bi := big.NewInt(max) + // prevent panic of rand.Int on big numbers + if bi.BitLen() < 2 { + return 0, errors.New("overflow") + } + i, err := rand.Int(rand.Reader, bi) + if err != nil { + return 0, err + } + return i.Uint64(), nil } func (g *WatGame) Heal(player *Player, fields []string) string { @@ -166,8 +175,11 @@ func (g *WatGame) Dice(player *Player, fields []string) string { roll = i } } - answer := g.RandInt(int64(roll)) + 1 - return fmt.Sprintf("1d%d - %d", roll, answer) + answer, err := g.RandInt(int64(roll)) + if e := handleError(err); e != "" { + return e + } + return fmt.Sprintf("1d%d - %d", roll + 1, answer) } type PositiveError struct{} @@ -206,12 +218,20 @@ func (g *WatGame) Roll(player *Player, fields []string) string { } lotteryNum := int64(-1) if dieSize > 100 { - lotteryNum = int64(g.RandInt(dieSize)) + 1 + lotteryNumRand, randErr := g.RandInt(dieSize) + if e := handleError(randErr); e != "" { + return e + } + lotteryNum = int64(lotteryNumRand) + 1 } if amount > player.Coins { return "wat? brokeass" } - n := int64(g.RandInt(dieSize)) + 1 + nRand, randErr := g.RandInt(dieSize) + if e := handleError(randErr); e != "" { + return e + } + n := int64(nRand) + 1 ret := fmt.Sprintf("%s rolls the %d sided die... %d! ", player.Nick, dieSize, n) if n == lotteryNum { player.Coins += player.Coins @@ -255,11 +275,18 @@ func (g *WatGame) Punch(player *Player, fields []string) string { if !target.Conscious() { return "wat? you're punching someone who is already unconscious. u crazy?" } - chance := g.RandInt(6) + 1 - dmg := g.RandInt(6) + 1 + chance, randErr := g.RandInt(6) + if e := handleError(randErr); e != "" { + return e + } + dmg, randErr := g.RandInt(6) + if e := handleError(randErr); e != "" { + return e + } + dmg = dmg + 1 ret := fmt.Sprintf("%s rolls a d6... %s ", player.Nick, player.Nick) dmg += uint64(player.Level(player.Anarchy)) - if chance > 3 { + if chance + 1 > 3 { ret += fmt.Sprintf("hits %s for %d points of damage! ", target.Nick, dmg) target.Health -= int64(dmg) g.db.Update(target) @@ -300,7 +327,11 @@ func (g *WatGame) Frame(player *Player, fields []string) string { if target.Coins < amount { return fmt.Sprintf("wat? %s is too poor for this.", target.Nick) } - n := g.RandInt(6) + 1 + n, randErr := g.RandInt(6) + if e := handleError(randErr); e != "" { + return e + } + n = n + 1 ret := fmt.Sprintf("%s rolls a d6 to frame %s with %d %s: It's a %d! (<3 wins). ", player.Nick, target.Nick, amount, currency, n) if n < 3 { ret += fmt.Sprintf("You frame %s for a minor crime. They pay me %d.", target.Nick, amount) @@ -335,7 +366,11 @@ func (g *WatGame) Steal(player *Player, fields []string) string { if target.Coins < amount { return fmt.Sprintf("wat? %s is poor and doesn't have that much to steal. (%d %s)", target.Nick, target.Coins, currency) } - n := g.RandInt(6) + 1 + n, randErr := g.RandInt(6) + if e := handleError(randErr); e != "" { + return e + } + n = n + 1 ret := fmt.Sprintf("%s is trying to steal %d %s from %s... ", player.Nick, amount, currency, target.Nick) if n < 3 { ret += "You did it! Sneaky bastard!" @@ -380,7 +415,11 @@ func (g *WatGame) Leech(player *Player, fields []string) string { if err != "" { return err } - r := g.RandInt(10) + 1 + r, randErr := g.RandInt(10) + if e := handleError(randErr); e != "" { + return e + } + r = r + 1 reply := fmt.Sprintf("You muster your wealth and feed it to %s. ", g.bot.Nick) hpDown := amount / divisor player.Coins -= amount @@ -408,7 +447,11 @@ func (g *WatGame) Rest(player *Player, fields []string) string { } else if delta < minRest { ret = fmt.Sprintf("wat were you thinking, sleeping at a time like this (%d until next rest)", minRest-delta) } else { - value := g.RandInt(10) + 1 + value, randErr := g.RandInt(10) + if e := handleError(randErr); e != "" { + return e + } + value = value + 1 if player.Health < -5 { player.Health = 1 ret = fmt.Sprintf("wow ur beat up. i pity u, ur health is now 1.") @@ -435,8 +478,15 @@ func (g *WatGame) Bench(player *Player, fields []string) string { if !g.CanAct(player, Action_Lift, minTime) { return "you're tired. no more lifting for now." } - weight := g.RandInt(370) + 50 - reps := g.RandInt(10) + weight, randErr := g.RandInt(370) + if e := handleError(randErr); e != "" { + return e + } + weight = weight + 50 + reps, randErr := g.RandInt(10) + if e := handleError(randErr); e != "" { + return e + } value := int64(0) reply := fmt.Sprintf("%s benches %dwatts for %d reps, ", player.Nick, weight, reps) if weight < 150 { @@ -452,7 +502,10 @@ func (g *WatGame) Bench(player *Player, fields []string) string { } if g.roid[player.Nick] != 0 { delete(g.roid, player.Nick) - success := g.RandInt(2) + success, randErr := g.RandInt(2) + if randErr != nil { + return "woops" + } if success != 0 { player.Health = 0 player.Anarchy -= 10 @@ -475,7 +528,10 @@ func (g *WatGame) Riot(player *Player, fields []string) string { if !g.CanAct(player, Action_Riot, int64((48 * time.Hour).Seconds())) { return "Planning a riot takes time and the right circumstances. Be prepared. (nothing happens)" } - r := g.RandInt(100) + r, randErr := g.RandInt(100) + if e := handleError(randErr); e != "" { + return e + } reply := "" if r > 40 { player.Anarchy += 3 @@ -621,9 +677,21 @@ func PrintTwo(nick string, value uint64) string { } func (g *WatGame) megaWat(player *Player, _ []string) string { - mega := g.RandInt(1000000) + 1 - kilo := g.RandInt(1000) + 1 - ten := g.RandInt(100) + 1 + mega, randErr := g.RandInt(1000000) + if e := handleError(randErr); e != "" { + return e + " mega fail" + } + mega = mega + 1 + kilo, randErr := g.RandInt(1000) + if e := handleError(randErr); e != "" { + return e + " kilo fail" + } + kilo = kilo + 1 + ten, randErr := g.RandInt(100) + if e := handleError(randErr); e != "" { + return e + " ten fail" + } + ten = ten + 1 reply := "" if mega == 23 { player.Coins += 1000000 diff --git a/wat/utils.go b/wat/utils.go new file mode 100644 index 0000000..331f7ad --- /dev/null +++ b/wat/utils.go @@ -0,0 +1,22 @@ +package wat + +import ( + "fmt" + "runtime" +) + +func handleError(err error) string { + if err != nil { + pc, _, _, ok := runtime.Caller(1) + details := runtime.FuncForPC(pc) + var cFun string + if ok && details != nil { + cFun = details.Name() + } else { + cFun = "???" + } + fmt.Printf("caught error in %s: %v\n", cFun, err) + return "u wat" + } + return "" +}