First take a look at how slices are stored internally:
It should give you an idea why it doesn't work.
Yes, a single dimensional array would be better.
Essentially write your C function as:
getMatrix(unsigned char *matrix, int ncols, int nrows)
And keep the Go matrix in a single array []byte.
Also ensure that array doesn't get prematurely garbage collected.
sources := make([]byte, 100)
data := unsafe.Pointer(&sources[0])
C.getMatrix((*C.uchar)(data), 10, 10)
_ = data
I can't remember the exact rules how to properly pass pointers to C, but it probably is covered in some thread/page.
+ egon