Skip to content

Commit

Permalink
Fix potential use-after-free with item metadata (minetest#12729)
Browse files Browse the repository at this point in the history
This fixes a use-after-free bug in the case where itemstack metadata is accessed after the itemstack has been garbage-collected.
  • Loading branch information
TurkeyMcMac committed Sep 11, 2022
1 parent 7486f18 commit fe13f9d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 26 deletions.
13 changes: 2 additions & 11 deletions src/script/lua_api/l_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
int LuaItemStack::gc_object(lua_State *L)
{
LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1));
delete o;
o->drop();
return 0;
}

Expand Down Expand Up @@ -152,7 +152,7 @@ int LuaItemStack::l_get_meta(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaItemStack *o = checkobject(L, 1);
ItemStackMetaRef::create(L, &o->m_stack);
ItemStackMetaRef::create(L, o);
return 1;
}

Expand Down Expand Up @@ -438,15 +438,6 @@ LuaItemStack::LuaItemStack(const ItemStack &item):
{
}

const ItemStack& LuaItemStack::getItem() const
{
return m_stack;
}
ItemStack& LuaItemStack::getItem()
{
return m_stack;
}

// LuaItemStack(itemstack or itemstring or table or nil)
// Creates an LuaItemStack and leaves it on top of stack
int LuaItemStack::create_object(lua_State *L)
Expand Down
13 changes: 8 additions & 5 deletions src/script/lua_api/l_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#include "lua_api/l_base.h"
#include "inventory.h" // ItemStack
#include "util/pointer.h"

class LuaItemStack : public ModApiBase {
class LuaItemStack : public ModApiBase, public IntrusiveReferenceCounted {
private:
ItemStack m_stack;

LuaItemStack(const ItemStack &item);
~LuaItemStack() = default;

static const char className[];
static const luaL_Reg methods[];

Expand Down Expand Up @@ -138,11 +142,10 @@ class LuaItemStack : public ModApiBase {
static int l_peek_item(lua_State *L);

public:
LuaItemStack(const ItemStack &item);
~LuaItemStack() = default;
DISABLE_CLASS_COPY(LuaItemStack)

const ItemStack& getItem() const;
ItemStack& getItem();
inline const ItemStack& getItem() const { return m_stack; }
inline ItemStack& getItem() { return m_stack; }

// LuaItemStack(itemstack or itemstring or table or nil)
// Creates an LuaItemStack and leaves it on top of stack
Expand Down
16 changes: 13 additions & 3 deletions src/script/lua_api/l_itemstackmeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ ItemStackMetaRef* ItemStackMetaRef::checkobject(lua_State *L, int narg)

Metadata* ItemStackMetaRef::getmeta(bool auto_create)
{
return &istack->metadata;
return &istack->getItem().metadata;
}

void ItemStackMetaRef::clearMeta()
{
istack->metadata.clear();
istack->getItem().metadata.clear();
}

void ItemStackMetaRef::reportMetadataChange(const std::string *name)
Expand All @@ -67,6 +67,16 @@ int ItemStackMetaRef::l_set_tool_capabilities(lua_State *L)
return 0;
}

ItemStackMetaRef::ItemStackMetaRef(LuaItemStack *istack): istack(istack)
{
istack->grab();
}

ItemStackMetaRef::~ItemStackMetaRef()
{
istack->drop();
}

// garbage collector
int ItemStackMetaRef::gc_object(lua_State *L) {
ItemStackMetaRef *o = *(ItemStackMetaRef **)(lua_touserdata(L, 1));
Expand All @@ -76,7 +86,7 @@ int ItemStackMetaRef::gc_object(lua_State *L) {

// Creates an NodeMetaRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side.
void ItemStackMetaRef::create(lua_State *L, ItemStack *istack)
void ItemStackMetaRef::create(lua_State *L, LuaItemStack *istack)
{
ItemStackMetaRef *o = new ItemStackMetaRef(istack);
//infostream<<"NodeMetaRef::create: o="<<o<<std::endl;
Expand Down
17 changes: 10 additions & 7 deletions src/script/lua_api/l_itemstackmeta.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#include "lua_api/l_base.h"
#include "lua_api/l_metadata.h"
#include "lua_api/l_item.h"
#include "irrlichttypes_bloated.h"
#include "inventory.h"

class ItemStackMetaRef : public MetaDataRef
{
private:
ItemStack *istack = nullptr;
LuaItemStack *istack;

static const char className[];
static const luaL_Reg methods[];
Expand All @@ -44,12 +44,12 @@ class ItemStackMetaRef : public MetaDataRef

void setToolCapabilities(const ToolCapabilities &caps)
{
istack->metadata.setToolCapabilities(caps);
istack->getItem().metadata.setToolCapabilities(caps);
}

void clearToolCapabilities()
{
istack->metadata.clearToolCapabilities();
istack->getItem().metadata.clearToolCapabilities();
}

// Exported functions
Expand All @@ -58,12 +58,15 @@ class ItemStackMetaRef : public MetaDataRef
// garbage collector
static int gc_object(lua_State *L);
public:
ItemStackMetaRef(ItemStack *istack): istack(istack) {}
~ItemStackMetaRef() = default;
// takes a reference
ItemStackMetaRef(LuaItemStack *istack);
~ItemStackMetaRef();

DISABLE_CLASS_COPY(ItemStackMetaRef)

// Creates an ItemStackMetaRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side.
static void create(lua_State *L, ItemStack *istack);
static void create(lua_State *L, LuaItemStack *istack);

static void Register(lua_State *L);
};
14 changes: 14 additions & 0 deletions src/util/pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,17 @@ class SharedBuffer
unsigned int *refcount;
};

// This class is not thread-safe!
class IntrusiveReferenceCounted {
public:
IntrusiveReferenceCounted() = default;
virtual ~IntrusiveReferenceCounted() = default;
void grab() noexcept { ++m_refcount; }
void drop() noexcept { if (--m_refcount == 0) delete this; }

// Preserve own reference count.
IntrusiveReferenceCounted(const IntrusiveReferenceCounted &) {}
IntrusiveReferenceCounted &operator=(const IntrusiveReferenceCounted &) { return *this; }
private:
u32 m_refcount = 1;
};

0 comments on commit fe13f9d

Please sign in to comment.