By definition of the [] subscript operator, the expression
a[b]
is equivalent to:
*(a+b)
This means that the expression
m[3][3]
is equivalent to:
*( (*(m+3)) + 3 )
Since you defined m to be a pointer to a pointer to an int, the expression m[3][3] will do the following: It will treat m as if it were a pointer to the first element of an array whose elements are of type int *. It will attempt to read the fourth element of this array. It will treat this fourth element as a pointer to the first element of an array whose elements are of type int and it will attempt to read the fourth element of this array.
However, m[3] has an indeterminate ("garbage") value, so m[3] does not point to a valid array. Therefore, by dereferencing this invalid pointer in the expresson m[3][3], your program is invoking undefined behavior, which means that anything can happen, including the behavior that you described in the question.
Because you used the line
m=(int **)malloc(10*10*sizeof(int));
I assume that it was not your intent to make m point to the first element of an array whose elements are of type int *, but rather to make m point to a 2D array of int elements. In other words, m should point to the first element of an array whose elements are not pointers, but arrays.
In order to accomplish this, you must declare the variable m differently.
The line
int **m;
will declare the variable m to be a pointer to a pointer to an int. This is not what you want. You want it to point to the first sub-array of the 2D array, so you want to declare a pointer to an array of 10 int elements. For this, you must use the following syntax:
int (*m)[10];
The parentheses are important, because without them, you would be declaring an array of 10 elements of type int *, but you want to instead declare a pointer to an array of 10 int elements.
Your program should look like this:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
    int (*m)[10];
    m = malloc( 10*10*sizeof(int) );
    if ( m == NULL )
    {
        fprintf( stderr, "Memory allocation failure!\n" );
        exit( EXIT_FAILURE );
    }
    m[3][3]=3;
    printf( "%d", m[3][3] );
    free( m );
}
This program has the following output:
3
Note that in C, in constrast to C++, it is not necessary and generally discouraged to cast the result of malloc.
Also, it is generally a good idea to check the return value of malloc, to verify that the memory was successfully allocated. Additionally, you should generally free the memory when you no longer need it.