Skip to content

Commit ddb071c

Browse files
authored
fix several critical security vulnerabilities
* discard unexpected network data safely * validate hotbar slot from client * validate entity id bounds when accessing mob data * prevent manipulation of memory pointer through crafting grid Credit to @vmpr0be and @loot01 for finding and reporting these issues.
1 parent 8e4d402 commit ddb071c

7 files changed

Lines changed: 51 additions & 7 deletions

File tree

include/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ typedef struct {
233233
// 0x10 - eating, makes flagval_16 act as eating timer
234234
// 0x20 - client loading, uses flagval_16 as fallback timer
235235
// 0x40 - movement update cooldown
236+
// 0x80 - craft_items lock (for storing pointers)
236237
uint8_t flags;
237238
} PlayerData;
238239

include/tools.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ static inline int div_floor (int a, int b) {
1515
extern uint64_t total_bytes_received;
1616
ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first);
1717
ssize_t send_all (int client_fd, const void *buf, ssize_t len);
18+
void discard_all (int client_fd, size_t remaining, uint8_t require_first);
1819

1920
ssize_t writeByte (int client_fd, uint8_t byte);
2021
ssize_t writeUint16 (int client_fd, uint16_t num);

src/crafting.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88

99
void getCraftingOutput (PlayerData *player, uint8_t *count, uint16_t *item) {
1010

11+
// Exit early if craft_items has been locked
12+
if (player->flags & 0x80) {
13+
*count = 0;
14+
*item = 0;
15+
return;
16+
}
17+
1118
uint8_t i, filled = 0, first = 10, identical = true;
1219
for (i = 0; i < 9; i ++) {
1320
if (player->craft_items[i]) {

src/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ void handlePacket (int client_fd, int length, int packet_id, int state) {
192192
case 0x1B:
193193
if (state == STATE_PLAY) {
194194
// Serverbound keep-alive (ignored)
195-
recv_all(client_fd, recv_buffer, length, false);
195+
discard_all(client_fd, length, false);
196196
}
197197
break;
198198

@@ -466,7 +466,7 @@ void handlePacket (int client_fd, int length, int packet_id, int state) {
466466
if (packet_id < 16) printf("0");
467467
printf("%X, length: %d, state: %d\n\n", packet_id, length, state);
468468
#endif
469-
recv_all(client_fd, recv_buffer, length, false);
469+
discard_all(client_fd, length, false);
470470
break;
471471

472472
}
@@ -476,7 +476,7 @@ void handlePacket (int client_fd, int length, int packet_id, int state) {
476476
if (processed_length == length) return;
477477

478478
if (length > processed_length) {
479-
recv_all(client_fd, recv_buffer, length - processed_length, false);
479+
discard_all(client_fd, length - processed_length, false);
480480
}
481481

482482
#ifdef DEV_LOG_LENGTH_DISCREPANCY

src/packets.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,8 @@ int cs_clickContainer (int client_fd) {
638638
} else
639639
#endif
640640
{
641+
// Prevent accessing crafting-related slots when craft_items is locked
642+
if (slot > 40 && player->flags & 0x80) return 1;
641643
p_item = &player->inventory_items[slot];
642644
p_count = &player->inventory_count[slot];
643645
}
@@ -811,7 +813,10 @@ int cs_setHeldItem (int client_fd) {
811813
PlayerData *player;
812814
if (getPlayerData(client_fd, &player)) return 1;
813815

814-
player->hotbar = (uint8_t)readUint16(client_fd);
816+
uint8_t slot = readUint16(client_fd);
817+
if (slot >= 9) return 1;
818+
819+
player->hotbar = slot;
815820

816821
return 0;
817822
}
@@ -845,6 +850,8 @@ int cs_closeContainer (int client_fd) {
845850
}
846851
player->craft_items[i] = 0;
847852
player->craft_count[i] = 0;
853+
// Unlock craft_items
854+
player->flags &= ~0x80;
848855
}
849856

850857
givePlayerItem(player, player->flagval_16, player->flagval_8);

src/procedures.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ void resetPlayerData (PlayerData *player) {
6868
player->craft_items[i] = 0;
6969
player->craft_count[i] = 0;
7070
}
71+
player->flags &= ~0x80;
7172
}
7273

7374
// Assigns the given data to a player_data entry
@@ -342,6 +343,13 @@ void spawnPlayer (PlayerData *player) {
342343

343344
task_yield(); // Check task timer between packets
344345

346+
// Clear crafting grid residue, unlock craft_items
347+
for (int i = 0; i < 9; i++) {
348+
player->craft_items[i] = 0;
349+
player->craft_count[i] = 0;
350+
}
351+
player->flags &= ~0x80;
352+
345353
// Sync client inventory and hotbar
346354
for (uint8_t i = 0; i < 41; i ++) {
347355
sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, i), player->inventory_count[i], player->inventory_items[i]);
@@ -425,7 +433,9 @@ void broadcastPlayerMetadata (PlayerData *player) {
425433
// If client_fd is -1, broadcasts to all player
426434
void broadcastMobMetadata (int client_fd, int entity_id) {
427435

428-
MobData *mob = &mob_data[-entity_id - 2];
436+
int mob_index = -entity_id - 2;
437+
if (mob_index < 0 || mob_index >= MAX_MOBS) return;
438+
MobData *mob = &mob_data[mob_index];
429439

430440
EntityData *metadata;
431441
size_t length;
@@ -1266,6 +1276,8 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t
12661276
// is mutually exclusive with chests, though it is otherwise a
12671277
// terrible idea for obvious reasons.
12681278
memcpy(player->craft_items, &storage_ptr, sizeof(storage_ptr));
1279+
// Flag craft_items as locked due to holding a pointer
1280+
player->flags |= 0x80;
12691281
// Show the player the chest UI
12701282
sc_openScreen(player->client_fd, 2, "Chest", 5);
12711283
// Load the slots of the chest from the block_changes array.
@@ -1420,7 +1432,9 @@ void interactEntity (int entity_id, int interactor_id) {
14201432
PlayerData *player;
14211433
if (getPlayerData(interactor_id, &player)) return;
14221434

1423-
MobData *mob = &mob_data[-entity_id - 2];
1435+
int mob_index = -entity_id - 2;
1436+
if (mob_index < 0 || mob_index >= MAX_MOBS) return;
1437+
MobData *mob = &mob_data[mob_index];
14241438

14251439
switch (mob->type) {
14261440
case 106: // Sheep
@@ -1551,7 +1565,10 @@ void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t da
15511565

15521566
} else { // The attacked entity is a mob
15531567

1554-
MobData *mob = &mob_data[-entity_id - 2];
1568+
int mob_index = -entity_id - 2;
1569+
if (mob_index < 0 || mob_index >= MAX_MOBS) return;
1570+
MobData *mob = &mob_data[mob_index];
1571+
15551572
uint8_t mob_health = mob->data & 31;
15561573

15571574
// Don't continue if the mob is already dead

src/tools.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ ssize_t send_all (int client_fd, const void *buf, ssize_t len) {
135135
return sent;
136136
}
137137

138+
void discard_all (int client_fd, size_t remaining, uint8_t require_first) {
139+
while (remaining > 0) {
140+
size_t recv_n = remaining > MAX_RECV_BUF_LEN ? MAX_RECV_BUF_LEN : remaining;
141+
ssize_t received = recv_all(client_fd, recv_buffer, recv_n, require_first);
142+
if (received < 0) return;
143+
if (received > remaining) return;
144+
remaining -= received;
145+
require_first = false;
146+
}
147+
}
148+
138149
ssize_t writeByte (int client_fd, uint8_t byte) {
139150
return send_all(client_fd, &byte, 1);
140151
}

0 commit comments

Comments
 (0)