How to read a string from a C function into Go? - c

I'm trying to call a C function from Go with cgo to read an error message. The function produces a message of an unknown length less than 256 bytes.
Working example in C:
char message[ERROR_SIZE]; //256
last_error( message, sizeof(message) );
printf( "message: %s\n", message );
My attempt in Go (not working):
var ptr *C.char
C.last_error(ptr, ERROR_SIZE)
var message = C.GoString(ptr)
fmt.Printf("message: %s\n", message)
When the go code is run, the message is empty. Does the go version need to preallocate space for the message? How to do this?
Update after comment by LPs to pass an array. This works, but seems a bit awkward:
var buf [ERROR_SIZE]byte
var ptr = (*C.char)(unsafe.Pointer(&buf[0]))
C.last_error(ptr, len(buf))
var message = C.GoString(ptr)
fmt.Printf("message: %s\n", message)
Is there a simpler way?

In your first example you are passing a nil pointer, so there is no allocated memory for C.last_error to write the output to (and luckily, it appears to just do nothing).
You need to allocate the memory somehow, and the most straightforward way to do that in Go is to use a slice, rather than create an array with a static size.
buf := make([]byte, ERROR_SIZE)
C.last_error((*C.char)(unsafe.Pointer(&buf[0])), len(buf))
// While C.GoString will find the terminating null if it's there,
// there's no reason to copy the string in C, and allocate another slice.
if i := bytes.IndexByte(buf, 0); i >= 0 {
buf = buf[:i]
}
fmt.Printf("message: %s\n", buf)

Related

LLVM IR -- how to convert array store to memcpy?

LLVM IR includes arrays as a base type, so a "store" instruction in IR will take an array object and store it to a pointer to memory.
I'm compiling to a C environment, so I need to convert "store" instructions to calls to memcpy. I've tried to use IRBuilder to make the job easier, but I'm stuck on how to take the address of an object.
The function I've written is as follows:
bool convert_array_store_to_memcpy(llvm::StoreInst *instruction)
{
llvm::Type *value_type = instruction->getValueOperand()->getType();
if (!value_type->isArrayTy())
return false;
/* set up IRBuilder and get the pieces of the store */
llvm::IRBuilder<> Builder(llvm::getGlobalContext());
Builder.SetInsertPoint(instruction);
llvm::Value *destination = instruction->getPointerOperand();
llvm::Value *source = instruction->getValueOperand();
/* get the number of bytes by getting the size of the array (elements*element-size) */
llvm::ArrayType *array_type = cast<ArrayType>(value_type);
uint64_t element_count = array_type->getNumElements();
llvm::Type *element_type = array_type->getElementType();
DataLayout *targetData = new DataLayout(mod);
uint64_t element_size = targetData->getTypeAllocSize(element_type);
uint64_t size = element_count*element_size;
/* PROBLEM: I am trying to take the address of the start of the array */
llvm::Type *i32_type = llvm::IntegerType::getInt32Ty(llvm::getGlobalContext());
llvm::Constant *constant_int = llvm::ConstantInt::get(i32_type, 0, true);
Value *indexList[1] = {constant_int};
/* NEW PROBLEM:indexList seems to be the wrong type or contain the wrong type of thing */
llvm::Value *pointer_to_source = Builder.CreateGEP(source, ArrayRef<Value*>(indexList, 1));
unsigned alignment = instruction->getAlignment();
if (!array_type)
fprintf(stderr, "ERROR!\n");
/* insert the memcpy */
llvm::CallInst *memcpy_call = Builder.CreateMemCpy(destination,
pointer_to_source,
size,
alignment,
instruction->isVolatile());
/* erase the store */
instruction->eraseFromParent();
return true;
} /* convert_array_store_to_memcpy */
This compiles, but I get the following runtime error from the call to IRBuilder::CreateGEP:
.../llvm/install/include/llvm/IR/Instructions.h:782: llvm::Type
*llvm::checkGEPType(llvm::Type *): Assertion `Ty && "Invalid GetElementPtrInst indices for type!"' failed.
Note that I'm using LLVM 3.6 under Linux.
EDIT: clearly, the call to createGEP is sending a null instead of the constant zero -- the intent was to get the address of the zeroth element of the array. I've edited the above function with my latest effort, which is to try to send a length-1 array of indices into createGEP. This is also failing inside of getIndexedType, which returns a NULL pointer, which I, again, don't understand.
Note: I am using the example from a previous StackOverflow answer: Inserting GetElementpointer Instruction in LLVM IR

Calling a C function with a double pointer output parameter using CGo

I am trying to figure out th right way to call this function:
size_t
fz_buffer_storage(fz_context *ctx, fz_buffer *buf, unsigned char **datap)
{
if (datap)
*datap = (buf ? buf->data : NULL);
return (buf ? buf->len : 0);
}
using CGo to get the underlying string and its length as a byte array in Go.
Is this the right way to do it?
var bufferContents *C.uchar
length := C.fz_buffer_storage(ctx, buf, &bufferContents)
bytes := C.GoBytes(unsafe.Pointer(bufferContents), C.int(length))
Since the C code overwrites *datap, I am not sure if the garbage collector will still do the right thing.
I saw an answer here suggesting something along the lines of
var tempUcharPtr *C.uchar
bufferContents := C.malloc(C.size_t(unsafe.Sizeof(tempUcharPtr)))
defer C.free(bufferContents)
length := C.fz_buffer_storage(ctx, buf, (**C.uchar)(bufferContents))
bytes := C.GoBytes(unsafe.Pointer(*(**C.uchar)(bufferContents)), C.int(length))
which also seems to work, but it's much more convoluted and I'm wondering if it's better / safer than the previous version.
Apparently, the first version is fine. Quoting the docs:
Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.
From what I understand, since var bufferContents *C.uchar will be initialised to nil, it does not count as a "Go pointer" for the above rule. The following simplified code examples confirm this:
package main
// void F(char **p) {}
import "C"
func main() {
var p *C.char = new(C.char)
C.F(&p)
}
will trigger "panic: runtime error: cgo argument has Go pointer to Go pointer"
package main
// void F(char **p) {}
import "C"
func main() {
var p *C.char
C.F(&p)
}
works just fine, even when setting GODEBUG=cgocheck=2.
Thanks to the folks on the #cgo channel on the Gophers Slack community for helping me understand this!

Malloc/free double free or corruption error

I have a problem with a simple malloc/free functions I use in a more complex program and I can't find how to get rid of this problem.
My project looks like :
main.c
while(1){programm();}
I tried a lot of tests to know where it come from but I just can't find a solution...
here is the code part where it seems to bug :
programm.c
void programm(){
... Creating variables and getting infos from socket ...
char a[512];
char b[512];
sprintf(a,"blablabla",strlen(a));
sprintf(b,"blablabla",strlen(b));
char* MessageOut = NULL;
MessageOut = (char*)malloc(strlen(a)+strlen(b));
if(MessageOut==NULL)
printf("MessageOut Is Null\n");
else
printf("%x\n",(uint)MessageOut);
printf("Size of Malloc:%d\n",strlen(a)+strlen(b));
sprintf( (char*)MessageOut, "%s%s",a, b );
MessageOut[0] = 0x02;
MessageOut[1] = Data[1];
MessageOut[2] = Data[2];
MessageOut[3] = 0x03;
byte_nb = sendto(client_socket, (void *)MessageOut, strlen(a)+strlen(b), 0, (struct sockaddr *)&dist_addr, addr_len);
if (byte_nb == -1) {
printf("send error:%s\n", strerror(errno));
} else {
printf("%i bytes sent\n", byte_nb);
}
printf("%s\n",MessageOut);
if(MessageOut==NULL)
printf("MessageOut Is Null\n");
else
printf("%x\n",(uint)MessageOut);
free(MessageOut);
printf("Test\n");
}
As I said it is just a part of my code, I tried to summarize it to the part where it goes wrong.
All of this is in a while(1)-loop.
The error I got is double free or corruption (!prev)
The printf give me :
1c7eeb0
Size Of Malloc : 196
196 Bytes sent
1c7eeb0
The first loop works correctly but after a few one I got
Error: double free or corruption (!prev): 0x01c7eeb0
It does not seems to be a problem with the socket because I have the same address before and after the sendto.
Here
sprintf(a,"blablabla",strlen(a));
strlen() is passed an uninitialised a which invokes undefined behaviour.
To initially set a initialise it on definition:
char a[512] = "blablabla";
or set it right after:
char a[512];
strcpy(a, "blablabla");
(The same applies to b)
Assuming a and b were set correctly this call
sprintf( (char*)MessageOut, "%s%s",a, b );
would write 1 char beyond MessageOut bounds, as after setting the data as per a and b and additional '\0' will be put, the so called 0-terminator, that every C-"string" carries to maker is end.
To fix this adjust the related call to malloc() accordingly:
MessageOut = malloc(strlen(a) + strlen(b) + 1); /* There is no need to cast
the result fo malloc in C. */

Can't free() a char*

void* password_cracker_thread(void* args) {
cracker_args* arg_struct = (cracker_args*) args;
md5hash* new_hash = malloc (sizeof(md5hash));
while(1)
{
char* password = fetch(arg_struct->in);
if(password == NULL )
{
deposit(arg_struct->out,NULL);
free(new_hash);
pthread_exit(NULL);
}
compute_hash(password,new_hash);
if(compare_hashes(new_hash,(md5hash**)arg_struct->hashes,arg_struct->num_hashes) != -1)
{
printf("VALID_PASS:%s \n",password);
deposit(arg_struct->out,password);
}else{
free(password);
}
}
}
This is a part of a program, where you get char* passwords from a ringbuffer, calculate md5 and compare them and push them into the next buffer if valid.
My problem is now, why can't I free those I don't need?
The whole program will stop if I try to and if I don't, I get memory leaks.
"You", and by this I mean your program, can only free() storage that was got from malloc()-and-friends, and only in the granularity it was got, and only once per chunk of storage.
In the code you show here, you're attempting to free() something got from fetch(). Since we can't see the definition of that function and you have not provided any documentation of it, our best guess is that
fetch() gives you a pointer to something other than a whole chunk
got from malloc()-et-al; and/or
some other part of the program not
shown here free()s the relevant chunk itself.

Expression: (L "String is not null terminated" & & 0)

I am fiddling around with mailslots and now I've run into a problem.
Whenever I try to run, I get the error message in the title, but I don't know how I should go about fixing it.
What I am trying to do is "fixing" the full path of the mailslot, but it seems to not like the strcat_s-part.
HANDLE mailslotCreate (char *name) {
char fullName[50] = "\\\\.\\mailslot\\";
strcat_s(fullName, strlen(fullName), name);
return CreateMailslot(fullName, 0, TIME_OUT, NULL);
}
Imgur link to error
EDIT: Changing the strlen to sizeof merely changed the error to "Buffer size too small" instead.
See documentation on strcat_s. It says that second parameter should be the size of destination buffer. As you pass strlen(fullName), there is no room for terminating \0.
Change it to be sizeof(fullName) and your error should disappear.

Resources