If I am correct that you wanted to understand what happened in your specific case, you could improve your question by providing the version of the compiler, the arguments you passed to the compiler, the arguments you passed to your program, and the output of your program. That way, you would have a Minimal Reproducible Example and we would understand better what your specific case is.
For example, I use GCC 9.4.0:
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Here is what happened when I compiled without optimization and passed a string with 55 characters as an argument to the program:
$ gcc -o bufferoverflow bufferoverflow.c
$ ./bufferoverflow 1234567890123456789012345678901234567890123456789012345
$
So, even though the number of bytes copied into the buffer, 56 including the terminator, should cause a write past the end of the buffer, the program ran without any error that is visible by simply looking at standard error or standard output.
Here is what happened when I ran the same executable but passed a 57 character string in the command line.
$ ./bufferoverflow 123456789012345678901234567890123456789012345678901234567
*** stack smashing detected ***: terminated
Aborted (core dumped)
$
One way to understand what happened in the case with the 55 character string is to run it again using using gdb, which can be started as shown:
$ gdb bufferoverflow
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bufferoverflow...
(No debugging symbols found in bufferoverflow)
(gdb)
Now lets see why passing a 55 character string as the first argument didn't result in an obvious failure:
(gdb) break main
Breakpoint 1 at 0x1169
(gdb) r 1234567890123456789012345678901234567890123456789012345
Starting program: /home/tim/bufferoverflow 1234567890123456789012345678901234567890123456789012345
Breakpoint 1, 0x0000555555555169 in main ()
(gdb) x/23i main
=> 0x555555555169 <main>: endbr64
0x55555555516d <main+4>: push %rbp
0x55555555516e <main+5>: mov %rsp,%rbp
0x555555555171 <main+8>: sub $0x50,%rsp
0x555555555175 <main+12>: mov %edi,-0x44(%rbp)
0x555555555178 <main+15>: mov %rsi,-0x50(%rbp)
0x55555555517c <main+19>: mov %fs:0x28,%rax
0x555555555185 <main+28>: mov %rax,-0x8(%rbp)
0x555555555189 <main+32>: xor %eax,%eax
0x55555555518b <main+34>: mov -0x50(%rbp),%rax
0x55555555518f <main+38>: add $0x8,%rax
0x555555555193 <main+42>: mov (%rax),%rdx
0x555555555196 <main+45>: lea -0x40(%rbp),%rax
0x55555555519a <main+49>: mov %rdx,%rsi
0x55555555519d <main+52>: mov %rax,%rdi
0x5555555551a0 <main+55>: callq 0x555555555060 <strcpy@plt>
0x5555555551a5 <main+60>: mov $0x0,%eax
0x5555555551aa <main+65>: mov -0x8(%rbp),%rcx
0x5555555551ae <main+69>: xor %fs:0x28,%rcx
0x5555555551b7 <main+78>: je 0x5555555551be <main+85>
0x5555555551b9 <main+80>: callq 0x555555555070 <__stack_chk_fail@plt>
0x5555555551be <main+85>: leaveq
0x5555555551bf <main+86>: retq
From the above disassembly we can see that main+60 is just after the call to strcpy. We can also see, by looking at main+45 and main+52 that the buffer is at %rbp-0x40. We can continue to that point and look at what happened to the buffer:
(gdb) b *(main+60)
Breakpoint 2 at 0x5555555551a5
(gdb) c
Continuing.
Breakpoint 2, 0x00005555555551a5 in main ()
(gdb) x/56bx $rbp-0x40
0x7fffffffdf90: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
0x7fffffffdf98: 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x36
0x7fffffffdfa0: 0x37 0x38 0x39 0x30 0x31 0x32 0x33 0x34
0x7fffffffdfa8: 0x35 0x36 0x37 0x38 0x39 0x30 0x31 0x32
0x7fffffffdfb0: 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30
0x7fffffffdfb8: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
0x7fffffffdfc0: 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x00
So we can see that, in spite of the fact that when we ran with this string earlier without gdb we didn't notice any obvious error, in fact the buffer overflow did occur. We simply didn't notice that it had. To understand why we didn't notice, one only has to look at the disassembly to see that the next used address on the stack is at %rbp-8 which is 56 bytes after %rbp-0x40. So the overflow went onto memory that was not in use.
The same disassembly shows why we get the stack smashing detected message when we run the program with the 57 character string. In that case, we clobber part of the 8-byte value at %rbp-8 which is used (at main+19, main+28, main+65, main+69 and main+78) as a check for whether the stack got corrupted during the call to main. So the reason we see that particular error with that particular input is that the 8-byte value at %rbp-8 was the only part of the stack that we clobbered that was actually used after we clobbered it and the message in question was as a result of noticing that those 8 bytes had changed.
Even if you did not compile your program exactly the way I did, and even if you did not use exactly the same input, I hope I have given you some solid ideas about how to understand the behavior in your case.