3

I'm writing a code to find a proper input that would generate a certain output for the SHA-1 hash function.

The problem I'm running into is that my code raises a segmentation fault, but gdb finds that it raises the following error upon entering main() and befor execution of any other code:

Program received signal SIGSEGV, Segmentation fault.
__strncpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:636
636 ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.

This is my code:

#include <iostream>
#include <cstdlib>
#include <cstring>

#include "sha1.hpp"

int main() {
    char *prefix = "SHA1sha1";
    char *suffix = "chicken and beer";
    std::string hashvalue = "nonzero";
    char *line = "just some dummy string";
    int loop_number = 0;

    while (hashvalue.c_str()[0] != '0' || hashvalue.c_str()[1] != '0') {
        // change prefix
        strncpy(prefix, hashvalue.c_str(), 8);
        // hash the concatenated string of prefix and suffix
        strncpy(line, prefix, 8);
        strncat(line, suffix, strlen(suffix));
        hashvalue = sha1(line);

        loop_number++;
        if (loop_number % 1000 == 0) std::cout << loop_number << "th loop, hash value: " << hashvalue << std::endl;
    }

    std::cout << "Found prefix: " << prefix << " with hash value: " << hashvalue << std::endl;

    return 0;
}

The sha1.hpp was not implemented by me, but was taken from here: http://www.zedwood.com/article/cpp-sha1-function

I have changed sha1.h to sha1.hpp, though, but this is probably not what's causing the segmentation fault.

Now I have tried searching for a solution to this problem with the error message and also with the keywords "segmentation fault before main", and this post seems to be going through similar problems: Segmentation Fault before main

However, I have looked into the two suggested solutions, but couldn't find one that fits me.

  1. I don't think my code has too many variables in the stack. In fact, I have tried commenting out using the function sha1() just in case, but the same problem occurred.

  2. I have initialized all char* and std::string in my code before usage.

FYI, I'm using g++ to compile my C++ codes.

Any help or push in the right direction would be greatly appreciated.

Saxtheowl
  • 4,136
  • 5
  • 23
  • 32
WannabeArchitect
  • 1,058
  • 2
  • 11
  • 22
  • 3
    Your code doesn't need to use any `char *` variables whatsoever. Everything can be `std::string`, and thus be much safer. – PaulMcKenzie Oct 10 '19 at 14:29
  • 1
    @PaulMcKenzie is right, you should be *very* circumspect about C++ code that uses legacy C strings. Not that you *can't,* just that it's rarely a good idea if you're coding C++ exclusively. – paxdiablo Oct 10 '19 at 14:31
  • @paxdiablo Thanks for the heads up, but the answers below (which I'm still trying to process in my head) say that string literals cannot be modified. It seems like legacy C strings are easier to code with but unsafe...? (I know they're unsafe, since I've had far too many faults by using them. I just want to know I'm on the right page here) – WannabeArchitect Oct 10 '19 at 14:34
  • 1
    C-style strings are harder to work with, not easier! You have to manage their lifetimes and capacities, which you don't with `std::string`. – Toby Speight Oct 10 '19 at 14:35
  • @TobySpeight Oh, sorry. I got confused. I thought string literals were std::string, but it was the other way around... Thanks! – WannabeArchitect Oct 10 '19 at 14:37
  • 3
    If you had turned on your compiler warnings, the compiler itself would have pointed out the mistakes in the code. – Eljay Oct 10 '19 at 14:38
  • @Eljay Actually, I did have them on... But I thought this type of warning could go without having to be dealt with. – WannabeArchitect Oct 10 '19 at 14:40
  • `std::string`s can be indexed, so you can write `hashvalue[0]`. – molbdnilo Oct 10 '19 at 14:52
  • "I thought this type of warning could go without having to be dealt with" - you don't have to *fix* them (they are warnings, after all) but you should *investigate* them at least :-) A good developer will try to remove warnings as well as errors, even if that means temporarily turning off the warning for a small section of code (that's hopefully proof that it's been investigated and deemed okay). – paxdiablo Oct 11 '19 at 02:31

2 Answers2

4

String literals have types of constant character arrays in C++, and any attempt to modify a string literal results in undefined behavior. You are trying to modify a string literal at least in this statement:

strncpy(prefix, hashvalue.c_str(), 8);

Instead of string literals you should use character arrays or objects of the type std::string.

Pay attention to that; for example this if statement

while (hashvalue.c_str()[0] != '0' || hashvalue.c_str()[1] != '0') {

could be written much simpler:

while (hashvalue[0] != '0' || hashvalue[1] != '0') {

though it seems the condition does not make sense. Maybe you mean the following

while ( hashvalue.size() > 1 ) {

Also you need to include the header <string>

#include <string>
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • For the part about the condition being weird, that's not the case. But thanks for the answer! It really helped. – WannabeArchitect Oct 10 '19 at 14:43
  • You state that modifying a string literal is UB. However, I think for this program to have UB it doesn't even have to do that. `prefix` and others not being `const` in itself is UB if I'm not mistaken. –  Oct 10 '19 at 14:54
  • 1
    @Chipster Declaring a pointer to a string literal without the qualifier const per se is not undefined behavior though the Standard does not allow this. A compiler can allow this for the backward compatibility. – Vlad from Moscow Oct 10 '19 at 14:59
4

You are modifying the immutable contents.

// change prefix
strncpy(prefix, hashvalue.c_str(), 8);
// hash the concatenated string of prefix and suffix
strncpy(line, prefix, 8);
strncat(line, suffix, strlen(suffix)); 

Try to change the declaration as below.

char prefix[100] = "SHA1sha1";
char suffix[200] = "chicken and beer";
char line[200] = "just some dummy string

Also, I guess

while (hashvalue.c_str()[0] != '0' || hashvalue.c_str()[1] != '0') {

should be

while (hashvalue.c_str()[0] != '0' && hashvalue.c_str()[1] != '0') {

Update:

De Morgan's law states,

not (A and B) = not A or not B

Again it is individual's choice to use whatever form they want

kiran Biradar
  • 12,700
  • 3
  • 19
  • 44