r/ProgrammerHumor 2d ago

Meme bugIntroducedDebugging

Post image
1.1k Upvotes

120 comments sorted by

349

u/AdBrave2400 2d ago

Is the mistake that they're allocating 1 byte and storing in a pointer to an int which is 4 bytes?

156

u/atanasius 2d ago

It's allowed to assign to a pointer if it's not dereferenced. Malloc always returns a valid pointer.

53

u/vishal340 2d ago

but accessing it will be issue right?

95

u/SoldRIP 2d ago

that's undefined.

2

u/nullpotato 1d ago

Undefined aka works fine on a dev build but explodes in release because all the debug info the compiler injects aren't there anymore.

37

u/backfire10z 2d ago

Likely not. Malloc guarantees that the passed-in size is the minimum number of bytes it allocates, but not the maximum. You’d likely get some minimum sized chunk, somewhere between 16-32 bytes depending on the system as far as I understand it.

41

u/atanasius 2d ago

Implementations are allowed to specify behavior that is otherwise undefined.

I looked up a C24 draft and it's actually stricter:

The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and size less than or equal to the size requested.

So a pointer returned by malloc(1) is not necessarily valid for int*.

-3

u/p88h 1d ago

Aligned means int sized at minimum.

The actual allocated size is much bigger anyways, but it will be a multiple of 8 bytes on a 64 bit system, and 'usable' part of that will be at least 8 bytes, and typically you will be able to read at least 16 without a page fault.

2

u/Deep-Piece3181 2d ago

That’s an implementation detail it’s still 100% UB

6

u/mckenzie_keith 2d ago

You are not allowed to de-reference a pointer after freeing it.

7

u/GoddammitDontShootMe 2d ago

Gemini added that return *x; that wasn't in the original code.

7

u/mckenzie_keith 2d ago

And then flagged it as a bug. It was so eager to find the bug that it added it into the code.

2

u/Niwrats 2d ago

who's gonna stop me?

24

u/EntitledPotatoe 2d ago

Malloc can return a null pointer if the operation fails. This can be the case if, afaik, for example, there is no more heap available and the OS is not capable of swapping or freeing some other memory for some reason

17

u/atanasius 2d ago

A null pointer is still valid to assign to a variable.

17

u/SeriousPlankton2000 2d ago

Also it's valid to call free(NULL)

3

u/meat-eating-orchid 2d ago

has it always been valid? I could have sworn that freeing a nullpointer was only valid in C++ but not in C. I couldn't find anything on cppreference on when this was introduced or if it has always been like this

10

u/GoddammitDontShootMe 2d ago

Very certain you could always free(NULL); in C. In fact, it's good practice to set pointers to NULL after freeing them to avoid issues like double-free.

3

u/EntitledPotatoe 2d ago

Very true, I thought you meant valid as in usable

29

u/CanardAuxEpices 2d ago

Uuuh... Actually 🤓🤓☝️☝️☝️ an int isn't 4 bytes, it's platform/compiler dependent. Assuming an int is 4 bytes will be in the majority of cases true, but it's not always the case

8

u/meat-eating-orchid 2d ago

technically, an int could even be just one byte if you are on an architecture where a byte (= smallest addressable unit) has 16 bit

4

u/CanardAuxEpices 2d ago

Yeah! But a char will always be one byte

2

u/mckenzie_keith 2d ago

We need to define terms more precisely. sizeof(char) will always be 1. But on some platforms, char is not an 8-bit type. It could actually be a 32 bit type. The c programming language does not (I believe) acknowledge or define what a byte is. But if it is an 8 bit integer type, then char will not always be one byte.

It may be better to say "octet" instead of "byte" in this type of discussion.

The implementation has to define CHAR_BIT in <limits.h>. CHAR_BIT is the number of bits in variables of type "char".

2

u/CanardAuxEpices 2d ago

You're brobably right. To be honest, I keep confusing myself with bytes and bit cause I'm french and here we use octet. Plus, my C classes starts getting quite far, but I just remembered the fun fact, I'll go check properly because it actually made me curious.

3

u/el_nora 2d ago

Fun fact, C does use the term byte all over the standard. And it is well defined. Byte is defined to be the unit that sizeof(char) is measured in. When the standard refers to bytes, they are referring to atomic divisions of memory in chunks of char.

Which does not necessarily need to be the same as an architectural byte. C requires only that their definition of byte is at least 8 bits (possibly more) and an addressable unit of memory - meaning it is some multiple of architectural bytes. Though on every platform still in use today, and not a historic curio, that multiple is 1.

1

u/mckenzie_keith 1d ago

So I am wrong about that. Oops. I guess then that one needs to define what they mean by "byte" when using it in an online discussion. The person I replied to is right, then, that char will always be one byte. But some people when they say "byte" are thinking of an 8-bit entity. Anyway, thanks for the info.

2

u/grencez 1d ago

It's kinda funny that C99 introduced int8_t and made it optional, but I'm sure there's some good reason. Basically if that type exists, then CHAR_BIT is 8. Also POSIX.1-2001 defines it that way, so most people can safely assume it.

119

u/mckenzie_keith 2d ago

De-referenced a pointer after freeing it. But the joke here is that the bug was not in the original code.

15

u/AdBrave2400 2d ago

I know

0

u/WisestAirBender 2d ago

The original code wasn't returning anything though. Isn't that a problem

17

u/mckenzie_keith 2d ago

No. main() doesn't need to return anything.

-6

u/critical_patch 2d ago

AFAIK technically it’s “undefined behavior” and will compile just fine

17

u/MegaIng 2d ago

main specifically is allowed to not return anything (the same way it's the one function that is allowed to never return)

0

u/Original-Ad-8737 1d ago

I learned recently that there is a kind of implicit return...

It will automatically assume that the return value is the last used variable or something like that.

Dont know specifics but i was weirded out why the compiler wasnt complaining about missing returns in some methods...

1

u/mckenzie_keith 1d ago

Only in main() as far as I know. All others the compiler should warn you.

4

u/nonlogin 2d ago

not at all

3

u/bloody-albatross 2d ago edited 2d ago

You're still on 32 bit? Are you doing a lot of embedded stuff?

Edit: I misread and thought they where referring to the pointer size.

13

u/AdBrave2400 2d ago

Isn't int 32-bit on most platforms and long long int is 64-bit?

18

u/Mateorabi 2d ago

That’s the neat part: it can depend!

sizeof() is your friend 

3

u/bloody-albatross 2d ago edited 2d ago

I read it as the size of the pointer being 4 bytes, but now I see you were referring to the int. AFAIK the size of int is specified as "at least" 32 bit in the C standard, and all 32 and 64 bit platforms I know indeed use 32 bit. Though the size of long (not speaking of long long) is different on different 64 bit platforms, IIRC. Windows uses 32 bit and Linux uses 64. Both use 64 for long long. I prefer to use (u)int*_t when I need to be sure about the size and I use int/size_t/time_t/... when I interface with functions that use those in their signature.

7

u/MattieShoes 2d ago

AFAIK the size of int is specified as "at least" 32 bit in the C standard

I believe it's 16 bit, though you're not likely to encounter a 16 bit int today unless it's a microcontroller or something.

3

u/SeriousPlankton2000 2d ago

Yes, long is 32 bit. A long long time ago we didn't need long long yet. I can still remember.

3

u/MattieShoes 2d ago edited 2d ago

And now there's __int_128t __int128_t, at least in gcc. :-)

So probably -170,141,183,460,469,231,731,687,303,715,884,105,728 to 170,141,183,460,469,231,731,687,303,715,884,105,727

That's what... undecillions?

3

u/bloody-albatross 2d ago

_ slipped, it's __int128_t :D

3

u/MattieShoes 2d ago

... you wrote the same thing? Yes two underscores at the start, but I wrote two underscores?

3

u/bloody-albatross 2d ago

You wrote __int_128t, I wrote __int128_t. These typos happen to me too.

→ More replies (0)

3

u/bloody-albatross 2d ago

Under 64 bit Windows. Under 64 bit Linux long is 64 bit. Don't know about macOS or other OSes. :D

4

u/yjlom 2d ago

The size of char (which is interchangeable with the size of a byte) is specified in old standards as large enough to a) be addressable without bit manipulations and b) hold the full C source character set, which need not be case sensitive and may rely on trigraphs. In practice, this comes out to at least 6 bits. In newer standards I believe it's specified as at least 8 bits.

The size of int short is at least and a multiple of that of char, the size of int is at least that of int short and a multiple of that of char, and so on.

So in theory, a conforming C implementation could have 6 bit ints.

2

u/bloody-albatross 2d ago

Right.

Oh and wchar_t is also weird. Its 16 bit under Windows and 32 bit under Linux.

1

u/conundorum 2d ago

wchar_t is messy, in large part because Windows was one of the earliest Unicode adopters. It's meant to be able to support any "wide" character, which means that it's supposed to be determined by the character pages the platform allows (and implicitly, should be 32 bits if any version of Unicode is supported, since Unicode is technically a 32-bit format regardless of encoding), but Windows adopting Unicode while it was still 16-bit UCS-2 and locking wchar_t down as a result meant that it was impossible for wchar_t to actually meet its requirements on Windows. ...Which meant that its minimum size requirement ended up being removed.

So, now its requirement is just "holds wide characters, check your implementation. Please don't use it."

1

u/conundorum 2d ago

Assuming 8-bit bytes, sizeof(int) is guaranteed to be a minimum of two, and intended to be the system's word size (which should technically be 8 for 64-bit platforms, but we're so used to 32-bit int that most platforms intentionally stagnate int at 32 bits & most processors have two native word sizes to accomodate), but can be anything higher. ILP64 models have 64-bit int, and there was at least one platform where all bytes were 64-bit and sizeof(char) == sizeof(long long) == 1, though, so it can get weird sometimes.

(Also, as a note, long long is required to be at least 64 bits. long is required to be at least 32 bits, and is meant to just be the 32-bit data type, but ends up being the design limitation fulcrum for most platforms; Windows is locked into 32-bit long because it needs to support 32-bit executables, and Linux is locked into 64-bit long because it needs to support punning pointers to long.)

1

u/P00lnoodl 2d ago

Yes but isn't the adress 8 bytes in a 64 bit system regardless of what it's pointing to?

1

u/mckenzie_keith 2d ago

x is a variable of type pointer to int. You can put x on the left side of a malloc() call. You can also free x. There is no bug up to that point.

1

u/crimsonroninx 2d ago

The mistake is using gemini for coding.

1

u/Duck_Devs 2d ago edited 2d ago

I believe it’s compliant to have malloc(1) return memory that can fit an int.

sizeof(char) is always 1; malloc works based on sizeof.

char is allowed to be anything >= 8 bits wide

int is >= 16 bits.

In theory, you could have char and int both be 16 bits.

Edit: this is why you should always do malloc(n * sizeof(type))

Edit 2: Wikipedia (C data types article) literally states that a platform could theoretically have all int types be 64 bits, for those who doubt me.

-5

u/[deleted] 2d ago

[deleted]

4

u/AdBrave2400 2d ago

The pointer is already allocated on the stack

254

u/wolfjazz93 2d ago

Allocating 1 byte and assigning to int ptr. 🥺😭😭

39

u/ClipboardCopyPaste 2d ago

uint8_t

17

u/yjlom 2d ago

char would be the correct answer, as the C standard doesn't mandate a byte be 8 bits (6 and 32 are very uncommon nowadays but not unheard of, and there's a few machines out there that get really creative).

6

u/mckenzie_keith 2d ago

So even on weird platforms where char is a 32 bit type, sizeof (char) is 1, right? I can't remember.

8

u/yjlom 2d ago

yeppers

2

u/AnnoyedVelociraptor 2d ago

FYI char in Rust is 32-bits, maximum size of a UTF-8 character

6

u/mckenzie_keith 2d ago

That is interesting. I know very little about rust.

2

u/cowslayer7890 2d ago

And in Java it's 16-bit, which means some Unicode characters get split in two

1

u/Xirdus 1d ago

Rust's char stores UTF-32, not UTF-8, but yes.

1

u/AnnoyedVelociraptor 1d ago

I did some more reading, and I wonder if it is more correct that Rust encodes Unicode Scalars as char?

1

u/Xirdus 1d ago

Technically speaking, the spec says char represents Unicode scalar, with actual encoding unspecified. The spec requires that char is 4 bytes and has the same ABI as u32, but that's it.

In practice, it's UTF-32. 

1

u/Moldat 1d ago

When you malloc a byte you get a page back anyway, so it doesn't matter much.

40

u/SeriousPlankton2000 2d ago

You asked them to find a bug.

You didn't ask them to find a bug in your code.

22

u/stupled 2d ago

If no bug add bug

18

u/inaem 2d ago

Joke from last year or recent, can't tell with how often Gemini updates their models. /s

19

u/1XRobot 2d ago

This is what it actually returns:

#include <stdlib.h>

int main(void) {
    int *x = malloc(sizeof(int)); // Allocates correct byte size for an int (typically 4 bytes)
    if (x == NULL) {
        return 1; // Good practice: Handle allocation failure
    }

    *x = 42; // Safe write operation

    free(x); // Correctly frees allocated block
    return 0;
}

44

u/PR8-E 2d ago

int* x = (int*) malloc(sizeof(int)); Since malloc returns void pointer you should parse it to type you use.

35

u/yjlom 2d ago

void * coerces to any pointer type. This is valid: int *x = malloc(sizeof(int));. This is also valid (in C23): auto x = (int *) malloc(sizeof(int));.

31

u/Mateorabi 2d ago

Get off my lawn with your newfangled auto keyword. 

7

u/ibevol 2d ago edited 2d ago

Cringe. It’s fine to use auto when the type is clear on the same line. In c++ the following is idiomatic:

auto ptr = std::make_unique<int>(42);

over specifying int twice

std::unique_ptr<int> ptr = std::make_unique<int>(42);

However, the following should not be considered idiomatic:

auto ptr = get_ambiguous_pointer();

It’s the same principle here.

1

u/yjlom 2d ago

The keyword ain't new, it's been used as a storage specifier since forever. What's new is overloading it for type inference.

5

u/Mateorabi 2d ago

Hey when you learned C in the 90s it’s “new”.  harumph. 

1

u/yjlom 2d ago

It's been there since the beginning, along with extern and static. You just don't tend to actually write it because it's the default inside functions and it doesn't make sense outside of them.

2

u/mydogatethem 2d ago

Is this different between C and C++? In C++ you absolutely cannot assign a void* to an int* without casting it. You can always do the reverse: a void* can be assigned any type of pointer.

3

u/vetgirig 2d ago

In C++ you do not really use "malloc", In C++ you use "new" instead.

1

u/mydogatethem 2d ago

I mean… yes you do, but not really. There are use cases for both. Not everything is a class and not every API you call is even going to work with that buffer you pass in unless the API can free() it. See posix for example.

Anyways, that wasn’t the question I asked and is not even relevant to the question I asked.

5

u/guiltysnark 2d ago

I heard you say you were interested in a bug

3

u/mbcarbone 2d ago

Fixed it … 🙃

3

u/Direct-Quiet-5817 2d ago

That thanks is meant to safeguard the coder during the future AI uprising.

11

u/AdBrave2400 2d ago

Didn't return anything from an int function in the original code

24

u/ATE47 2d ago

it’s not mandatory for main

9

u/AdBrave2400 2d ago

I thought that was specific to C++. Thanks I didn't know it's been a thing since C99

5

u/usa_reddit 2d ago

There are multiple bugs:

int *x = malloc(1); Dynamically allocates 1 byte of memory on the heap. This is bad because an int is usually 4 bytes, it should be

int *x = malloc(sizeof(int));

free(x) deallocates the memory on the heap thus *x becomes a dangling pointer, but the code will still run since it is pointing to the programs heap (allocated memory). It is just undefined behavior since you have no guarantee what is being stored at that memory location.

This is why pointers in C can be dangerous.

1

u/1XRobot 2d ago

Is there anything in this code that isn't a bug?

Oh wait, the line with just a }.

1

u/morimando 2d ago

“Use after free”, eh?
Isn’t that like one of the more prolific security issues? 😄

1

u/Ok_Reserve_8659 2d ago

I once asked Claude if it could install a node library version 1.1 from remote. I wrote the library and so The local copy was on the machine there but there was an issue connecting to the server . Claude decided to alias version 1.1 to the local copy instead of reporting that there was a connection error 😭

2

u/kartblanch 1d ago

To be fair the lack of a return is “a bug” there just happens to be multiple.

0

u/YeetYourSkeet 1d ago

I know this is just a joke, but you can make anything sound stupid when you make up a fake scenario to attach to it. If you actually ask AI, it correctly identifies the issue.

1

u/Cronos993 1d ago

Did Gemini give you that list of things that never happened?

1

u/gerbosan 1d ago

😓 and I was thinking the joke was asking Gemini as it is never taken seriously in the list of agents for developers.

Also have to man up, have to learn C.

1

u/SirMarkMorningStar 1d ago

What happened to new and delete? You kids get some wacky new libraries since I switched to Java (and then switched to JavaScript and Python)?

2

u/LostgamerFJ 2d ago

I'm not good enough at programming for this. What do the "free" and "malloc" functions do?

28

u/LucyShortForLucas 2d ago

They are core C functions. malloc allocates N bytes of memory, free frees that memory. * is t he dereference operator, so *x is trying to dereference freed memory, which is undefined behavior

3

u/DiodeInc 2d ago

I thought * was a pointer. Isn't it? How is it dereferencing freed memory?

Or is it because it's returning *x that's the bug?

8

u/Vimda 2d ago

* in a variable declaration incidates a pointer type, * in a statement is the dereference operator

2

u/DiodeInc 2d ago

I see. Thanks

3

u/ThomasScotford 2d ago

* is the dereference operator, but in the context of variable declaration, it specifies the variable is a pointer to a type.

-Thomas Scotford

1

u/ArjixGamer 2d ago

Why are you leaving a name signature in your comment, when your username is the same as that name?

4

u/LostgamerFJ 2d ago

That explains a lot. I mainly code in Java and python, as I'm a beginner and have never touched c

2

u/conundorum 2d ago

That makes sense. Java in particular does a lot of this under the hood; anything that isn't a primitive is actually a reference (special C++-style pointer with non-pointer syntax), and new is a combination of malloc() & objection creation. (delete should be a combination of object destruction & free(), but they had to change how it works to accomodate the garbage collector.)

C (and C++) just makes you do the actual bookkeeping yourself.

6

u/Independent_Spell_55 2d ago

If I remember correctly, Malloc allocates memory, and I assume free would free up that memory, so when the variable is returned it doesn’t exist. I don’t have much experience with manual memory management so take anything I said with a grain of salt.

1

u/Mateorabi 2d ago

“If I remember correctly”. Please hand in your programmer badge and compiler to the officer at the front desk. 

7

u/Scratch137 2d ago

"malloc" is used to allocate a given number of bytes in the heap. for example, int x* = malloc(5) allocates 5 bytes in the heap and sets x to the address of the first byte.

"free" releases heap-allocated memory. in this case, free(x) frees up the 5 bytes we allocated earlier.

2

u/abc9hkpud 2d ago edited 2d ago

Malloc allocates memory. It takes as input the number of bytes, so in this case you need something like

Int* x = (int) malloc(1sizeof(int))

For an int array of length 1. Free is used to free the pointer.

After free, you shouldn't access the memory (or return it for someone else to use)

Modern C++ uses new and delete instead. Malloc and free are used in C

1

u/JustACasualReddittor 2d ago

This is C. Malloc is "memory allocation" and it will essentially "reserve" a certain amount of memory to store data (an integer in this case) and return the adress of that allocated memory (known as pointer). So now you can point at that adress and read a value or write a new one.

Free is the reverse, it frees the memory space so now it can be used by something else in the program. In a lot of languages all memory is freed automatically after the program stops executing, but not C. If you don't free the memory other programs can't use it. (It gets freed eventually in modern computers tough)

3

u/yjlom 2d ago

Malloc works on top of OS mapping, which gets freed as soon as the program is terminated.

2

u/vetgirig 2d ago

Yes any memory allocated to a program is automatically returned to the operating system when the program exit and stop running.

However malloced memory who aint got a free in a server program will continue to increase the memory heap until the program is terminated with out of memory since all available memory has been used by the program. But that is just for the program.

Operating systems usually do not have that problem. Well, most operating systems do not have that problem. For example Windows 95 was prone to that error.

1

u/yjlom 2d ago edited 2d ago

Malloc(n) attempts to allocate a region of memory of size n bytes, preceded by a descriptor for free to use. It has its own memory buffer that it tries to use first; if it's out of memory, it asks the OS to allocate more RAM to the process; if that fails (due to running out of RAM or OS policy), it returns 0, aka NULL. If it succeeds at any point, it returns a pointer to just before the data (and just after the descriptor).

Free(p) will look for a malloc-written descriptor just in front of *p, and notify malloc that it's no longer in use and can be recycled.

This code is buggy because:

  • It allocates only 1 byte, while int usually takes 4 bytes (C allows for a byte to be any length at least 6 bits, while an int must just be a non-zero natural amount of bytes long). It should instead be malloc(sizeof(int)).
  • In the second example, it tries to read the pointer after free, but at that point malloc might already have reused the memory for something else or given it back to the OS.
  • It fails to check that malloc actually succeeded, which is ok here because it doesn't do anything with it, but any more complex program would crash or worse if it didn't.

0

u/MetaNovaYT 2d ago

They’re heap allocation and deallocation functions in C/C++. ‘malloc’ requests a specified number of bytes to be allocated by the OS, and it then returns a pointer to that memory which can be used to store any value (or values) you want. 

‘free’ tells the OS to deallocate that memory so it can be used for future allocations if needed, so after freeing the memory, the pointer from ‘malloc’ points to memory your program no longer has ownership of. 

The bug that Gemini added is trying to  read the value at that pointer after the memory has been deallocated, which will most often cause a crash because your program tried to access memory it doesn’t have access to anymore

2

u/Rajarshi1993 2d ago

In my experience, coding agents can typically catch this kind of simple bug accurately.